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]:
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