一.继承顺序
1 继承顺序
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 #新式类继承顺序:F->D->B->E->C->A #经典类继承顺序:F->D->B->A->E->C #python3中统一都是新式类 #pyhon2中才分新式类与经典类
1 class A(object): 2 def test(self): 3 print('from A') 4 5 class B(A): 6 def test(self): 7 print('from B') 8 9 class C(A): 10 def test(self): 11 print('from C') 12 13 class D(B): 14 def test(self): 15 print('from D') 16 17 class E(C): 18 def test(self): 19 print('from E') 20 21 class F(D,E): 22 # def test(self): 23 # print('from F') 24 pass 25 f1=F() 26 f1.test() 27 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 28 29 #新式类继承顺序:F->D->B->E->C->A 一条路走,走完走另一条路,最后走A 30 #经典类继承顺序:F->D->B->A->E->C 一条路走,中间走尖,回到起始,再走另一条路 31 #python3中统一都是新式类 32 #pyhon2中才分新式类与经典类
1 class A(object): 2 def test(self): 3 print('from A') 4 5 class M(A): 6 # def test(self): 7 # print('from M') 8 pass 9 class N(A): 10 def test(self): 11 print('from N') 12 class B(M): 13 # def test(self): 14 # print('from B') 15 pass 16 class C(N): 17 def test(self): 18 print('from C') 19 20 21 class E(C): 22 def test(self): 23 print('from E') 24 class D(B): 25 # def test(self): 26 # print('from D') 27 pass 28 29 class F(D,E): 30 # def test(self): 31 # print('from F') 32 pass 33 34 35 f1=F() 36 f1.test() 37 print(F.mro()) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 38 39 40 41 42 [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.M'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.N'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
二.子类中调用父类的方法
1.父类名.父类方法()
1 #_*_coding:utf-8_*_ 2 __author__ = 'jj' 3 4 class Vehicle: #定义交通工具类 5 Country='China' 6 def __init__(self,name,speed,load,power): 7 self.name=name 8 self.speed=speed 9 self.load=load 10 self.power=power 11 12 def run(self): 13 print('开动啦...') 14 15 class Subway(Vehicle): #地铁 16 def __init__(self,name,speed,load,power,line): 17 Vehicle.__init__(self,name,speed,load,power) 18 self.line=line 19 20 def run(self): 21 print('地铁%s号线欢迎您' %self.line) 22 Vehicle.run(self) 23 24 line13=Subway('中国地铁','180m/s','1000人/箱','电',13) 25 line13.run()
2.super()
1 class Vehicle: #定义交通工具类 2 Country='China' 3 def __init__(self,name,speed,load,power): 4 self.name=name 5 self.speed=speed 6 self.load=load 7 self.power=power 8 9 def run(self): 10 print('开动啦...') 11 12 class Subway(Vehicle): #地铁 13 def __init__(self,name,speed,load,power,line): 14 #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) 15 super().__init__(name,speed,load,power) 16 self.line=line 17 18 def run(self): 19 print('地铁%s号线欢迎您' %self.line) 20 super(Subway,self).run() 21 22 class Mobike(Vehicle):#摩拜单车 23 pass 24 25 line13=Subway('中国地铁','180m/s','1000人/箱','电',13) 26 line13.run()
使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表
1 #每个类中都继承了且重写了父类的方法 2 class A: 3 def __init__(self): 4 print('A的构造方法') 5 class B(A): 6 def __init__(self): 7 print('B的构造方法') 8 super(B,self).__init__() 9 10 11 class C(A): 12 def __init__(self): 13 print('C的构造方法') 14 super(C,self).__init__() 15 16 17 class D(B,C): 18 def __init__(self): 19 print('D的构造方法') 20 super(D,self).__init__() 21 22 f1=D() 23 24 print(D.__mro__) #python2中没有这个属性
super()传的第一个参数一定是自己的类名
py2
1 #encoding:utf-8 2 class People: 3 def __init__(self,name,sex,age): 4 self.name=name 5 self.age=age 6 self.sex=sex 7 def walk(self): 8 print('%s is walking' %self.name) 9 10 class Chinese(People): 11 country='China' 12 def __init__(self,name,sex,age,language='Chinese'): 13 super(Chinese,self).__init__(name,sex,age) #super(子类,self) 调用的对象的绑定方法,调用的从mro列表从父类的开始找 14 self.language=language 15 16 c=Chinese('egon','male',18) 17 print(c.name,c.age,c.sex,c.language)
py3
1 class People: 2 def __init__(self,name,sex,age): 3 self.name=name 4 self.age=age 5 self.sex=sex 6 def walk(self): 7 print('%s is walking' %self.name) 8 9 class Chinese(People): 10 country='China' 11 def __init__(self,name,sex,age,language='Chinese'): 12 super().__init__(name,sex,age) #super(子类,self) 调用的对象的绑定方法,调用的从mro列表从父类的开始找 13 self.language=language 14 def walk(self,x): 15 super().walk() 16 print('子类的x',x) 17 18 19 egon 18 male Chinese 20 egon is walking 21 子类的x 123
三.多态
多态:(定义角度)
同一种事物的多种形态
多态性:(使用角度)
一种调用方式,不同的执行效果
依赖于:
1.继承: 都有父类的特点
2.
定义统一的接口:可以传入不同类型的值,但是调用的逻辑都一样,执行的结果却不一样
def func(obj): #obj这个参数没有类型限制,可以传入不同类型的值
obj.run() #调用的逻辑都一样,执行的结果却不一样
func(peo1)
func(pig1)
python自带多态,默认支持多态
好处:
1.增加了程序的灵活性
2.增加了程序额外可扩展性
1 class Animal: 2 def run(self): 3 raise AttributeError('子类必须实现这个方法') 4 def speak(self): 5 raise AttributeError('子类必须实现这个方法') 6 7 class People(Animal): 8 def run(self): 9 print('人正在跑') 10 11 class Pig(Animal): 12 def run(self): 13 print('pig is running') 14 15 peo1=People() #创建一个人的对象 16 pig1=Pig() #创建一个猪的对象 17 18 peo1.run() 19 pig1.run() 20 21 def func(obj): 22 obj.run() 23 24 func(peo1) 25 func(pig1) 26 27 28 人正在跑 29 pig is running 30 人正在跑 31 pig is running
四.封装
封装数据的主要原因是:保护隐私(作为男人的你,脸上就写着:我喜欢男人,你害怕么?)
封装方法的主要原因是:隔离复杂度(快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了,比如你不必知道你自己的尿是怎么流出来的,你直接掏出自己的接口就能用尿这个功能)
封装其实分为两个层面,但无论哪种层面的封装,都要对外界提供好访问你内部隐藏内容的接口(接口可以理解为入口,有了这个入口,使用者无需且不能够直接访问到内部隐藏的细节,只能走接口,并且我们可以在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问)
第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装
注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口
第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
在python中用双下划线的方式实现隐藏属性(设置成私有的)
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
这种自动变形的特点:
- 类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
- 这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
- 在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了
也可以通过特性property来解决。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
1
2
3
4
|
>>> a = A() >>> a._A__N >>> a._A__X >>> A._A__N |
2.变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#正常情况 >>> class A: ... def fa( self ): ... print ( 'from A' ) ... def test( self ): ... self .fa() ... >>> class B(A): ... def fa( self ): ... print ( 'from B' ) ... >>> b = B() >>> b.test() from B |
#b.test---->B---->A b.fa()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#把fa定义成私有的,即__fa >>> class A: ... def __fa( self ): #在定义时就变形为_A__fa ... print ( 'from A' ) ... def test( self ): ... self .__fa() #只会与自己所在的类为准,即调用_A__fa ... >>> class B(A): ... def __fa( self ): ... print ( 'from B' ) ... >>> b = B() >>> b.test() from A |
#B-->A-->test.fa-->def __fa(self)
python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的
其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的,作为外部的你,一意孤行也是可以用的,只不过显得稍微傻逼一点点
python要想与其他编程语言一样,严格控制属性的访问权限,只能借助内置方法如__getattr__,详见面向对象进阶
1 class A: 2 __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N 3 def __init__(self): 4 self.__X=10 #变形为self._A__X 5 def __foo(self): #变形为_A__foo 6 print('from A') 7 def bar(self): 8 self.__foo() #只有在类内部才可以通过__foo的形式访问到.
1 class A: 2 x=1 3 def test(self): 4 print('from A') 5 6 7 print(A.x) 8 A.test(123) 9 a=A() 10 a.y=1 11 print(a.y) 12 a.test() #隔离复杂度 13 14 15 16 1 17 from A 18 1 19 from A
只是一种变形手段,提醒自己的
#__名字,这种语法,只在定义的时候才有变形的效果,如果类或者对象已经产生了,就不会有变形效果了
1 class A: 2 __x=1 #_A_x 3 def test(self): 4 print('from A') 5 6 # print(A.__x) 7 print(A.__dict__) 8 print(A._A__x) 9 a=A() 10 print(a._A__x) 11 12 13 14 15 {'test': <function A.test at 0x0000000000A3E1E0>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, '__dict__': <attribute '__dict__' of 'A' objects>, '_A__x': 1} 16 1 17 1
1 class A: 2 __x=1 #_A_x 3 def __test(self): 4 print('from A') 5 6 print(A.__dict__) 7 A._A__test(123) 8 9 a=A() 10 a._A__test() 11 12 13 14 {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'A' objects>, '_A__x': 1, '_A__test': <function A.__test at 0x0000000000B2E1E0>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} 15 from A 16 from A
原:
1 class A: 2 def fa(self): 3 print('from A') 4 def test(self): 5 self.fa() 6 7 class B(A): 8 def fa(self): 9 print('from B') 10 11 b=B() 12 b.test() #b.test---->B---->A b.fa() 13 14 15 16 17 from B
1 class A: 2 def __init__(self): 3 self.__x=1 4 a=A() 5 print(a.__dict__) 6 print(a._A__x) 7 #print(a.__x) 8 9 10 11 12 13 {'_A__x': 1} 14 1
在继承中,父类不想让子类覆盖自己的方法,可以将方法定义为私有的。
改:
1 class A: 2 def __fa(self): #A__fa 3 print('from A') 4 def test(self): 5 self.__fa() #self._A__fa 6 7 class B(A): 8 def __fa(self): #B__fa 9 print('from B') 10 b=B() 11 b.test() #B-->A-->test.fa-->def __fa(self) 12 13 14 15 16 17 from A