Python:封装、继承、多态、私有成员

Python作为一种面向对象语言,也有着面向对象的三种特性:封装、继承、多态。

封装

将事物相关的属性和方法封装在某一个类里面,使得在调用类创建实例时,可以不用关心类内部的实现细节。

类的本质
   1. 是将属性(全局变量),方法(函数)都封装在一个黑盒子里面;
   2. 类里面的方法可以共享属性,属性的修改不会影响类的外部变量,这就是类的封装产生的优势;
   3. 同时类可以被继承,子类可以拥有和父类一样的属性和方法;
   4. 并且子类可以有新的属性,新的方法。

在类的内部,可以设置私有变量、私有方法:在一般变量或者方法前,加入两个下划线即可成为私有成员,但是不包括魔法方法(即前后都有双下划线的,因为魔法方法可以直接访问),私有成员一般不能外部直接访问,包括对象也不可以直接访问

继承

子类可以继承父类里面的属性或者方法,当然子类也可以提供自己的属性或方法,子类可以重写和覆盖父类方法或属性;python中可以多继承(此时继承的顺序很重要)。

多态:(有人说有两种多态,一种是继承多态,即子类继承自同一个父类;一种是鸭子多态,即子类继承自不同的父类)

(1)多态指的是对于同一个方法,不同对象调用时功能的表现形式不一样。

(2)python中的多态并不关注对象本身,而是关注它的功能和使用 —— 也即是有这个功能即可,并不需要共同继承一个父类(当然也可以继承自同一个父类)。

(3)在python有一个鸭子类型 (duck typing)用于实现多态,解释就是:如果一个动物走路、游泳、吃东西等都跟鸭子很像,就认为这个动物是鸭子,而不管它本身究竟是什么;

例如加法操作,对于整数类型和字符串类型来说,结果完全不一样;但是整数和字符串,却都是可以使用加法这个功能的。

总结:

  • 多态:可以对不同类的对象使用同样的操作。
  • 封装:对外部世界隐藏类内部的工作细节。
  • 继承:以普通的类为基础建立专门的类对象。

示例:

1. 封装和继承,也可以用 help(class_name) 获取类的基本信息

class grandfather(object):   #继承自基类 object,这个类很特别,因为所有class都会继承这个类
    ''' grandfather类的定义如下'''  #说明文档,获取grandfather.__doc__
    con_flag = 1   #普通静态字段
    __con_data = '爷爷'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):   #初始化方法,实例化的时候自动调用
        self.name = name
        self.age = age
        self.__id = id_
        print('grandfather类初始化完成')
    
    def __get_age(self):   #私有函数只能在类内使用
        return self.age
    
    def show_age(self):
        print(self.age)
        print('grandfather年龄显示完毕')

这个类 grandfather封装了一个静态变量、一个私有化变量、三个方法(一个魔法方法__init__,一个私有化方法__get_age,一个一般方法show_age)。

实例化对象时,会自动执行魔法方法__init__,当然注意:如果该方法有其他参数(除self外),那么实例化对象时必须要传入参数才行。

上述类中,类定义的(object)表示继承;__init__方法实例化时自动执行,且第一个参数self表示创建的类实例本身,不需要传入这个参数,而是使用时会自动填充当前对象;在__init__方法中,可以把很多属性绑定到self中,因为它指向的就是实例本身。

注意:这里的名称也不一定要用self,可以换成其他的,只要是第一个参数就行,只不过默认用这个self代表自身比较直观。

if __name__ == '__main__':
    grandfather1 = grandfather('g1',70,'002')  #实例化对象
    grandfather1.show_age()    #调用方法

2. 继承:子类继承父类的属性和方法,如果子类中有同名的属性或者方法,则子类会覆盖父类的属性或方法;如果子类中找不到某属性或方法,会去父类中查找;多继承时,有查找顺序(python的C3算法计算的顺序,通过类名.mro() 或者 类型.__mro__可获取顺序)。

示例:

class grandfather(object):
    ''' grandfather类的定义如下'''
    con_flag = 1   #普通静态字段
    __con_data = '爷爷'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):
        self.name = name
        self.age = age
        self.__id = id_
        print('grandfather类初始化完成')
        
    def __get_age(self):   #私有函数只能在类内使用
        return self.age
    
    def show_age(self):
        print(self.age)
        print('grandfather年龄显示完毕')

class father(grandfather):  #继承自grandfather类
    ''' father类的定义如下'''
    con_flag = 2   #普通静态字段
    __con_data = '爸爸'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):
        self.name = name
        self.age = age
        self.__id = id_
        print('father类初始化完成')
    
    def show_age(self):
        print(self.age)
        print('father年龄显示完毕')

class son(father):   #继承自father类
    ''' son类的定义如下'''
    con_flag = 3   #普通静态字段
    __con_data = '儿子'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):
        self.name = name
        self.age = age
        self.__id = id_
        print('son类初始化完成')
    
    def set_age(self, age):  #用于间接修改私有变量
        self.age = age
        
    def show_age(self):
        print(self.age)
        print('私有静态变量:',self.con_flag)
        print('私有静态变量:',self.__con_data)  #类内使用私有变量
        print('son年龄显示完毕')

使用时:

if __name__ == '__main__':
    son1 = son('Tom',12,'001')   #实例化,如果子类有__init__方法,就会使用子类的;否则使用父类的
    son1.show_age()
    print(son1.con_flag)     #对象直接访问非私有变量
    print(son1._son__con_data)   #对假私有变量进行访问
    
    son1.set_age('13')
    son1.show_age()
    
    son1.con_flag = 100     #可该方式可以直接修改
    print(son1.con_flag)
son1._son__con_data
= 1000 #可该方式可以直接修改私有变量 print(son1._son__con_data) son1.show_age() # print(son1.__con_data) #AttributeError: 'son' object has no attribute '__con_data',因为是私有变量,不能直接用

  • 首先,这里的子类中方法重写了父类的方法,相当于实例化时使用时直接用的是子类的__init__方法。
  • 其次,前面说了,私有成员不能直接访问,但是可以间接访问,(严格的说python的私有属于假私有),有以下两种方式:

(1)通过在类中设置访问私有成员的非私有函数,例如son类中的set_age 函数;

(2)通过对象来访问,方式是:对象名._类名__私有成员名,例如这里的:son1._son__con_data。

  • 而且,如果子类中没有__init__方法,但父类中有,那么实例化子类时,也需要传参数,因为子类会继承父类的属性和方法
class dog(father):
    ''' dog类的定义如下'''
    con_flag = 5   #普通静态字段
    __con_data = '狗狗'  #私有静态变量只能在类内使用
    
    def show_age(self):
        print(self.age)
        print('dog年龄显示完毕')

调用:

    dog1 = dog('dog1',5,'005')  #使用了父类的初始化函数__init__(),如果父类没有,会继续向上回溯查找,直到找到__init__方法或者都没有该方法
#    dog2 = dog()   #报错,因为父类中存在__init__方法
    print(dog1.name)
    dog1.show_age()

  • 如果,子类和父类及以上均没有__init__方法,那么实例化子类时不需要传参数
class cat(object):   # cat(dog)
    ''' cat类的定义如下'''
    con_flag = 6   #普通静态字段
    __con_data = ''  #私有静态变量只能在类内使用
    age = 10
    def show_age(self):
        print(self.age)
        print('cat年龄显示完毕')

class pig(cat):
    ''' pig类的定义如下'''
    con_flag = 7   #普通静态字段
    __con_data = ''  #私有静态变量只能在类内使用
    
    age = 5   #覆盖父类属性,因为是先查找子类,再依次向上查找
    
    def show_age(self):
        print(self.age)
        print('pig年龄显示完毕')

使用时:

    cat2 = cat()
    cat2.show_age()
    
    pig1 = pig()
    pig1.show_age()

3. 多态:两种多态形式 —— 继承多态和鸭子多态

class grandfather(object):
    ''' grandfather类的定义如下'''
    con_flag = 1   #普通静态字段
    __con_data = '爷爷'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):
        self.name = name
        self.age = age
        self.__id = id_
        print('grandfather类初始化完成')
        
    def __get_age(self):   #私有函数只能在类内使用
        return self.age
    
    def show_age(self):
        print(self.age)
        print('grandfather年龄显示完毕')

class father(grandfather):
    ''' father类的定义如下'''
    con_flag = 2   #普通静态字段
    __con_data = '爸爸'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):
        self.name = name
        self.age = age
        self.__id = id_
        print('father类初始化完成')
    
    def show_age(self):
        print(self.age)
        print('father年龄显示完毕')

class daughter(father):
    ''' daughter类的定义如下'''
    con_flag = 4   #普通静态字段
    __con_data = '女儿'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):
        self.name = name
        self.age = age
        self.__id = id_
        print('daughter类初始化完成')
    
    def show_age(self):
        print(self.age)
        print('daughter年龄显示完毕')

def show_func(obj):   #类似接口一样,不判断对象的类型,直接使用函数
    obj.show_age()

if __name__ == '__main__':
    print('--- 多态 ---')
    grandfather1 = grandfather('g1',70,'002')
    father1 = father('f1',34,'003')
    daughter1 = daughter('d1',14,'004')
    show_func(grandfather1)
    show_func(father1)
    show_func(daughter1)

很明显,这三个类继承的都不是一样的父类,但是却存在相同的方法(都重写了父类的方法),所以可以直接像接口一样,通过对象直接调用 —— 鸭子多态

如果子类时继承自相同的父类呢?不重写方法的话:

class son(father):
    ''' son类的定义如下'''
    con_flag = 3   #普通静态字段
    __con_data = '儿子'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):
        self.name = name
        self.age = age
        self.__id = id_
        print('son类初始化完成')

class daughter(father):
    ''' daughter类的定义如下'''
    con_flag = 4   #普通静态字段
    __con_data = '女儿'  #私有静态变量只能在类内使用
    def __init__(self,name,age,id_):
        self.name = name
        self.age = age
        self.__id = id_
        print('daughter类初始化完成')

使用时:

    print('---- 继承多态 ----')
    son2 = son('Jack',12,'001')
    daughter2 = daughter('Nancy',14,'004')
    son2.show_age()
    daughter2.show_age()

可见这两个实例化对象都调用了父类的相同方法,但是结果是不一致的 —— 继承多态

总结:

  • 私有成员在类外可以通过间接方式访问。
  • 类中的对象参数不一定要是self,可以是其他的。

python的多重继承下次探讨。

#

参考:

https://blog.51cto.com/13803166/2129725

https://www.cnblogs.com/semon-code/p/8242062.html

https://www.jianshu.com/p/c95bffc4700f

https://www.bbsmax.com/A/xl5697X0Jr/

https://blog.csdn.net/qq_38262155/article/details/80536263

https://blog.csdn.net/simuLeo/article/details/80067619

https://www.cnblogs.com/tanxu05/p/9904970.html

https://www.cnblogs.com/ksht-wdyx/p/11369188.html

原文地址:https://www.cnblogs.com/qi-yuan-008/p/12838959.html