Python-面向对象之多继承

1、Python不同版本的类

  Python2.2之前类是没有共同的祖先的,之后,引入object类,它是所有类的共同祖先类object。

  Python2 中为了兼容 ,分为古典类,新式类。

  Python2 中全部都是新式诶

  新式类都是继承自object的,新式类可以使用super

    

  Python2.2
  新式类:等同 python3,
   旧式类:不能使用super,只能使用 类名. 调用父类的方法属性, 没有base,只有bases
  Python3.x
  对2的统一, 一切都继承自 object,3 更像面向对象

  dir:尽量收集一个对象所有属性:dir(A):所有属性的列表
  
 1 In [4]: dir(A)
 2 Out[4]:
 3 ['__class__',
 4  '__delattr__',
 5  '__dict__',
 6  '__dir__',
 7  '__doc__',
 8  '__eq__',
 9  '__format__',
10  '__ge__',
11  '__getattribute__',
12  '__gt__',
13  '__hash__',
14  '__init__',
15  '__init_subclass__',
16  '__le__',
17  '__lt__',
18  '__module__',
19  '__ne__',
20  '__new__',
21  '__reduce__',
22  '__reduce_ex__',
23  '__repr__',
24  '__setattr__',
25  '__sizeof__',
26  '__str__',
27  '__subclasshook__',
28  '__weakref__']
29 
30 In [5]:
dir(A)

 2、多继承
  ocp原则:多用继承,少修改

  继承的用途:在子类上实现对基类的增强,实现多态

  多态:

     在面向对象中,父类,子类通过继承联系 在一起,如果可以通过一套方法,就可以实现不同表现,就是多态。

    一个类继承自多个类就是多继承,它将有多个类的特征

    多态必须建立在 继承和 覆盖才会体现!

 3、多继承弊端

  多继承很好的模拟了世界,但是会引起复杂性,带来冲突。多继承的实现会导致编译器设计的复杂度增加,所以现在很多语言舍弃了类的多继承。

  C++支持多继承, java舍弃多继承

  多继承可能会带来二义性。如:猫,狗继承了动物类的叫,但是子类继承谁的shout呢?

  解决方案:实现多继承的语言,要解决二义性,深度优先还是广度优先

 

                               图1

 

                                图2


                                        图3

Python 有三套解决方案:
2.2 早期:图2, 图3
经典算法,深度优先 如截图2,c 离得近,但是没法使用,只能先用A的X,但是新式类后来修改后,只保留重复的最后一个
即 M D B C A object
继承的单调性 不能保证
2.3版本解决了上面的问题:
C3 算法,并不是真正的 深度优先,可以检测有没有冲突性,单调性如果有二义性会抛异常

先看一下MRO 就明白搜索顺序了,以后多看看继承路径!(方法 属性都是一样的查找顺序)

因为动态语言,所以编译时才可能发现问题,此时已经晚了!!!,所以宁愿使用单继承,少使用多继承
        
       C3算法解决了多继承的二义性


  经典算法有很大的问题,如果C 中有覆盖A的方法,就不会访问到了,因为先访问A
  新式算法,还是深度优先,解决了重复问题,但是没有解决继承的单调性
  c3算法,解决了继承的单调性,它阻止了之前版本产生二义性的代码,求得MRO本质是为了线性化,且确定了顺序。

  MRO:方法解析顺序

3、多继承的缺点:
  当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径
  Python语法允许多继承,但Python代码时解释执行,只有执行到的时候,才会发现错误,此时已经晚了。
  不管语言是够支持多继承,都应避免多继承。
   Python的面向对象太灵活,太开放,所以要团队协作。

4、 Mixin混合功能:
  类有下面的继承关系:

          Pdf --------> Document <--------Word
  文档Document类是其他所有文档类的抽象基类:
  Word,Pdf类是Document的子类
  
  思路:

  1、需求为Document子类提供打印能力
1 class Document:
2     def __init__(self, content):
3         self.content = content
4 
5     def print(self):# 没有实现,完全抽象成一个方法
6         raise NotImplemented('未实现')
7 
8 class Word(Document): pass # 其他功能略去
9 class Pdf(Document): pass # 其他功能略去

  基类提供的方法不应该具体实现,因为他未必适合子类的打印,子类需要覆盖重写。

  基类中只定义了,不实现的方法,称为抽象方法 在Python中,如果采用这种方式定义的抽象方法,子类可以不实现,知道子类使用该方法的是才报错。
  
  print算是一种能力,打印功能,不是所有的Document的子类都需要的,所以,从这个角度出发,有点问题。

 2、需要打印的子类上增加
  如果在现有子类上直接增加,违反了Ocp原则,所以应该继承后增加:


 1 class Document:
 2     def __init__(self, content):
 3         self.content = content
 4 
 5     def print(self):# 没有实现,完全抽象成一个方法,这个方法是知道所有的子类必须实现。
 6         raise NotImplemented('未实现')
 7 
 8 class Word(Document): pass # 其他功能略去
 9 class Pdf(Document): pass # 其他功能略去
10 
11 
12 # 单继承 这就是OCP,不要在第三方库修改,继承下来,增强或修改
13 class PrintableWord(Word):
14     def print(self):
15         print(self.content)
16 
17 
18 print(PrintableWord.__dict__)
19 print(PrintableWord.mro())
20 
21 pw = PrintableWord('test string')
22 pw.print()
23 
24 # {'__module__': '__main__', 'print': <function PrintableWord.print at 0x0000000002960378>, '__doc__': None}
25 # [<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
26 # test string
  看似不错,如果需要还要提供其他的能力,如何继承?
  应用于网络,文档应该具备序列化能力,类上就应该实现序列化。
  可序列化还可能分为使用pickle,json,messagepack等

  这个时候发现,为了增加一种能力,就要增加一次继承,类可能太多了,继承的方式不是很好了。
  功能太多,A 类需要某几样功能,B类需要另几样功能,它们需要的是多个功能的自由组合,继承实现很繁琐。

  3、装饰器
  用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰它。
 1 class Document:
 2     def __init__(self, content):
 3         self.content = content
 4 
 5     def print(self):# 没有实现,完全抽象成一个方法
 6         raise NotImplemented('未实现')
 7 
 8 class Word(Document): pass # 其他功能略去
 9 class Pdf(Document): pass # 其他功能略去
10 
11 
12 def printable(cls):
13     def _print(self):
14         print(self.content)
15     cls.print = _print
16     return cls
17 
18 # 单继承 这就是OCP,不要在第三方库修改,继承下来,增强或修改
19 @printable # PrintableWord = printable(PrintableWord)=PrintableWord
20 class PrintableWord(Word):pass
21     # def print(self):
22     #     print(self.content)
23 
24 pw = PrintableWord('test string')
25 pw.print()

    注意:上图 如果在类里边,如果实例 self在 print方法中,直接调用print的话,就会出错,递归调用,自己调用自己。

  优点:

    简单方便,在需要的地方动态的加入,直接使用装饰器,可以为类灵活的增加功能

  4、Mixin

    举例:

 1 class Document:
 2     def __init__(self, content):
 3         self.content = content
 4 
 5     def print(self):
 6         raise NotImplemented
 7 
 8 class Word(Document):pass
 9 
10 # 简单的实现了一个Mixin 类,添加了一个打印功能
11 class PrintTableMixin:
12     def print(self):
13         print('{}:{}'.format(type(self).__name__, self.content))
14 
15 # 再对之前的Mixin再做修饰
16 class SuperPrintableMixin(PrintTableMixin):
17     def print(self):
18         print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
19         super().print()
20         print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
21 
22 # 继承了Mixin 类
23 class SuperPrintableWordMixin(SuperPrintableMixin, Word):pass
24 
25 # 实例化一个被修饰后的实例
26 swm = SuperPrintableWordMixin('test word')
27 swm.print()

  打印结果:

1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!
2 SuperPrintableWordMixin:test word
3 !!!!!!!!!!!!!!!!!!!!!!!!!!!!
打印结果
  Mixin就是其他类混合进来,同时带来了类的属性和方法。
  这里看来Mixin 类 和装饰器效果一样, 也没有什么特别的,但是Mixin就是类,就可以继承。!!!
  
  总结:
    Mixin本质上就是类,就是多继承实现的
    Mixin体现的是一种组合的设计模式
    在面向对象的设计中,一个复杂的类,往往需要很多的功能,而这些功能有来自不同的类提供,
    这就需要很多的类组合在一起
    从设计模式的角度来说,多组合,少继承
     
    Mixin 类的使用原则;
      • Mixin 类中不应该显示的出现__init__ 初始化方法
      • Mixin 类通常不能独立工作,因为他是准备混入别的类中的部分功能实现
      • Mixin 类的最先类也是Mixin类
    使用时:Mixin 类通常在继承列表的第一个位置,
        例如:class PrintWord(PrintMixin, Word):pass
        因为深度优先,如果放在右侧,可能会出现异常,如果其他父类也实现了该方法,但是功能不一定一样,所以导致结结果不同!

 

练习:

 1 import math
 2 import pickle
 3 
 4 class Shape:
 5     def __init__(self):
 6         pass
 7 
 8     def area(self):
 9         raise NotImplemented('未实现')
10 
11 # class ser:
12 #     def serialize(self):
13 #         s = pickle.dumps(self.area())
14 #         return s
15 #
16 # class circle(ser, Shape):
17 #     def __init__(self, r):
18 #         self.r = r
19 #
20 #     def area(self):
21 #         area =  math.pi * (self.r ** 2)
22 #         return  area
23 
24 def ser(cls):
25     def serialize(self):
26         s = pickle.dumps(self.area())
27         return s
28     cls.ser = serialize
29     return cls
30 
31 
32 @ser # cricle = ser(cricle)
33 class circle(Shape):
34     def __init__(self, r):
35         self.r = r
36 
37     def area(self):
38         area =  math.pi * (self.r ** 2)
39         return  area
40 
41 c = circle(4)
42 print(c.area())
43 print(c.ser())
44 print(circle.__dict__)
45 
46 
47 
48 
49 class triangle(Shape):
50     def __init__(self, a, b, c):
51         self.a = a
52         self.b = b
53         self.c = c
54     def area(self):
55         p = (self.a + self.b + self.c) / 2
56         q = math.sqrt(p * (p-self.a) * (p-self.b) * (p-self.c))
57         return p / q
58 
59 class square(Shape):
60     def __init__(self, a):
61         self.a = a
62 
63     def area(self):
64         return self.a * self.a
举例

 

 

 

 

 

 

 

 

为什么要坚持,想一想当初!
原文地址:https://www.cnblogs.com/JerryZao/p/9665563.html