面向对象编程(三)

时间:2020-11-26 17:21:33

一.继承顺序

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的形式:

 

这种自动变形的特点:

  1. 类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
  2. 这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
  3. 在子类定义的__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