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