Python不同版本的类
Python2.2之前是没有共同的祖先的,之后,引入object
类,它是所有类的共同祖先类object
。
Python2中为了兼容,分为古典类(旧式类)和新式类。
Python3中全部都是新式类
新式类都是继承自object
的,新式类可以使用super
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
class A: pass
class B(object): pass
print(dir(A)) print(dir(B)) print(A.__bases__) print(B.__bases__)
a = A() print(a.__class__) print(type(a))
b = B() print(b.__class__) print(type(b))
|
多继承
OCP原则:多用“继承”、少修改
继承的用途:增加基类、实现多态
多态
在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同表现,就是多态。
一个类继承自多个类就是多继承,它将具有多个类的特征。
多继承弊端
多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性,带来了冲突。
如同一个孩子继承了来自父母双方的特征。那么到底眼睛像爸爸还是妈妈呢?孩子究竟该像谁多一点呢?
多继承的实现会导致编译器设计的复杂度增加,所以现在很多语言也舍弃了类的多继承。
C++支持多继承;Java舍弃了多继承。
Java中,一个类可以实现多个接口,一个接口也可以继承多个接口。Java的接口很纯粹,只是方法的声明,继承者必须实现这些方法,就具有了这些能力,就能干什么。
多继承可能会带来二义性,例如,猫和狗都继承自动物类,现在如果一个类多继承了猫和狗类,猫和狗都有shout
方法,子类究竟继承谁的shout
呢?
解决方案
实现多继承的语言,要解决二义性,深度优先或者广度优先。
Python多继承实现
1 2
| class ClassName(基类列表): 类体
|
左图是多继承,右图是单一继承
多继承带来路径选择问题,究竟继承哪个父类的特征呢
Python使用MRO(method resolution order)解决基类搜索顺序问题。
历史原因,MRO有三个搜索算法:
经典算法,按照定义从左到右,深度优先策略。2.2之前
左图的MRO是MyClass,D,B,A,C,A
新式类算法,经典算法的升级,重复的只保留最后一个。2.2
左图的MRO是MyClass,D,B,C,A,object
C3算法,在类被创建出来的时候,就计算出一个MRO有序列表。2.3之后,Python3唯一支持的算法
左图中的MRO是MyClass,D,B,C,A,object的列表
C3算法解决多继承的二义性
多继承的缺点
当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径。
Python语法是允许多继承,但Python代码是解释执行,只有执行到的时候,才发现错误。
团队协作开发,如果引入多继承,那代码将不可控。
不管编程语言是否支持多继承,都应当避免多继承。
Python的面向对象,我们看到的太灵活了,太开放了,所以要团队守规矩。
Mixin
类有下面的继承关系
文档Document类是其他所有文档类的抽象基类;
Word、Pdf是Document的子类。
需求:为Document子类提供打印能力
思路:
- 在Document中提供print方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Document: def __init__(self, content): self.content = content def print(self): raise NotImplementedError()
class Word(Document): pass class Pdf(Document): pass ===================================================================================== raise 语句的基本语法格式为: raise [exceptionName [(reason)]]
=====================================================================================
|
基类提供的方法不应该具体实现,因为它未必适合子类的打印,子类中需要覆盖重写。
print
算是一种能力–打印功能,不是所有的Document
的子类都需要的,所以,从这个角度出发,有点问题。
- 需要打印的子类上增加
如果在现有子类上直接增加,违反了OCP的原则,所以应该继承后增加,因此有下图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Printable: def print(self): print(self.content) class Document: def __init__(self, content): self.content = content class Word(Document): pass class Pdf(Document): pass
class PrintableWord(Printable, Word): pass print(PrintableWord.__dict__) print(PrintableWord.mro())
pw = PrintableWord('test string') pw.print() print(pw.__dict__) 输出: {'__module__': '__main__', '__doc__': None} [<class '__main__.PrintableWord'>, <class '__main__.Printable'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>] test string {'content': 'test string'}
|
看似不错,如果还需要提供其他能力,如何继承?
应用于网络,文档应该具备序列化的能力,类上就应该实现序列化。
可序列化还可能分为使用pickle、json、messagepack等。
这个时候发现,类可能太多了,继承的方式不是很好了。
功能太多,A类需要某几样功能,B类需要另几样功能,很繁琐。
- 装饰器
用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| def printable(cls): def _print(self): print(self.content, '装饰器') cls.print = _print return cls
class Document: def __init__(self, content): self.content = content class Word(Document): pass class Pdf(Document): pass
@printable class PrintableWord(Word): pass print(PrintableWord.__dict__) print(PrintableWord.mro())
pw = PrintableWord('test string') pw.print()
@printable class PrintablePdf(Word): pass 输出: {'__module__': '__main__', '__doc__': None, 'print': <function printable.<locals>._print at 0x7f1114f72550>}
[<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>] test string 装饰器
|
优点:
简单方便,在需要的地方动态增加,直接使用装饰器
- Mixin
先看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Document: def __init__(self, content): self.content = content class Word(Document): pass class Pdf(Document): pass
class PrintableMixin: def print(self): print(self.content, 'Mixin') class PrintableWord(PrintableMixin, Word): pass print(PrintableWord.__dict__) print(PrintableWord.mro())
def printable(cls): def _print(self): print(self.content, '装饰器') cls.print = _print return cls
@printable class PrintablePdf(Word): pass print(PrintablePdf.__dict__) print(PrintablePdf.mro())
|
Mixin就是其它类混合进来,同时带来了类的属性和方法
这里看来Mixin类和装饰器效果一样,也没有什么特别的。但是Mixin是类,就可以继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class Document: def __init__(self, content): self.content = content class Word(Document): pass class Pdf(DOcument): pass
class PrintableMixin: def print(self): print(self.content, 'Mixin') class PrintableWord(PrintableMixin, Word): pass print(PrintableWord.__dict__) print(PrintableWord.mro())
pw = PrintableWord('test string') pw.print()
class SuperPrintableMixin(PrintableMixin): def print(self): print('~' * 20) super().print() print('~' * 20)
class SuperPrintablePdf(SuperPrintableMixin, Pdf): pass
print(SuperPrintablePdf.__dict__) print(SuperPrintablePdf.mro())
spp = SuperPrintablePdf('super print pdf') spp.print()
|
Mixin类
Mixin本质上就是多继承实现的
Mixin体现的是一种组合的设计模式。
在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起。
从设计模式的角度来说,多组合,少继承。
Mixin类的使用原则
- Mixin类中不应该显式的出现
__init__
初始化方法
- Mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
- Mixin类的祖先类也应是Mixin类
使用时,Mixin类通常在继承列表的第一个位置,例如class PrintableWord(PrintableMixin, Word): pass
Mixin类和装饰器
这两种方式都可以使用,看个人喜好
如果还需要继承就得使用Mixin类的方式。
练习
- Shape基类,要求所有子类都必须提供面积的计算,子类有三角形、矩形、圆。
- 上题圆类的数据可序列化
参考
- Shape基类,要求所有子类都必须提供面积的计算,子类有三角形、矩形、圆。
思路
既然是基类,那么就应该有一个东西大家共同继承是最好的。都需要计算面积,面积可以是一个属性,也可以是一个方法。属性有两种东西,一种是真正的属性,一种是property装饰的属性,装饰的属性就是个方法。计算面积本来就是个动作,每一个子类都应该继承父类这个动作,至少应该实现一个求面积的方法,如何让这个动作像个属性暴露给别人,一调用就出来呢,这就需要装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import math
class Shape: @property def area(self): raise NotImplementedError('基类未实现')
class Triangle(Shape): def __init__(self, a, b, c): self.a = a self.b = b self.c = c @property def area(self): p = (self.a+self.b+self.c)/2 return math.sqrt(p*(p-self.a)*(p-self.b)*(p-self.c)) class Rectangle(Shape): def __init__(self, width, height):
self.width = width self.height = height @property def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.d = radius * 2 @property def area(self): return math.pi * self.d * self.d * 0.25 shapes = [Triangle(3,4,5), Rectangle(3,4), Circle(4)] for s in shapes: print('The area of {} = {}'.format(s.__class__.__name__, s.area)) 输出: The area of Triangle = 6.0 The area of Rectangle = 12 The area of Circle = 78.53981633974483
|
- 圆类的数据可序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import json import msgpack
class SerializableMixin: def dumps(self, t='json'): if t == 'json': return json.dumps(self.__dict__) elif t == 'msgpack': return msgpack.packb(self.__dict__) else: raise NotImplementedError('没有实现的序列化') class SerializableCircleMixin(SerializableMixin, Circle): pass
scm = SerializableCircleMixin(4) print(scm.area) s = scm.dumps('msgpack') print(s) s = scm.dumps('json') print(s) 输出: 50.26548245743669 b'\x81\xa1d\x08' {"d": 8}
|
作业
用面向对象实现LinkedList链表
单向链表实现append
、iternodes
方法
双向链表实现append
、pop
、insert
、remove
、iternodes
方法
参考
对于链表来说,每一个结点是一个独立的对象,结点自己知道内容是什么,下一跳是什么。而链表则是一个容器,它内部装着一个个结点对象。所以,建议设计2个类,一个结点Node类,一个链表LinkedList类。
将链表的整体看作一个对象,是一个容器,容器中间放的是相同的,只是实例不同而已。实例有两个属性,一个是插入的内容,一个是内容指向
单向链表1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
| class SingleNode: """ 代表一个节点,存储节点Node """ def __init__(self, val, next=None):
self.val = val self.next = next def __str__(self): return str(self.val)
__repr__ = __str__
class LinkedList: """ 容器类,按某种方式存储一个个节点 """ def __init__(self): self.nodes = [] self.head = None self.tail = None def append(self, val):
node = SingleNode(val)
prev = self.tail
if prev is None:
self.head = node
else: prev.next = node
self.nodes.append(node)
self.tail = node
a = LinkedList() a.append(3) print(a) print(a.nodes) 输出: <__main__.LinkedList object at 0x7ff960e6be50>
[<__main__.SingleNode object at 0x7f57cad638b0>]
==================================================================================== class SingleNode: """ 代表一个节点,存储节点Node """ def __init__(self, val, next=None, prev=None):
self.val = val self.next = next self.prev = prev def __repr__(self): return str(self.val) def __str__(self): return str(self.val)
class LinkedList: """ 容器类,按某种方式存储一个个节点 """ def __init__(self): self.nodes = []
self.head = None self.tail = None
def __len__(self): return len(self.nodes) def append(self, val):
node = SingleNode(val)
prev = self.tail
if prev is None:
self.head = node
else: prev.next = node
self.nodes.append(node)
self.tail = node
def iternodes(self, reverse=False): current = self.head while current: yield current current = current.next def __getitem__(self, item): return self.nodes[item] ll = LinkedList() node = SingleNode(5) ll.append(node) node = SingleNode(7) ll.append(node)
for node in ll.iternodes(): print(node) print(ll[1]) 输出: 5 7 7 ==================================================================================== class SingleNode: def __init__(self, item, next=None): self.item = item self.next = next def __repr__(self): return repr(self.item) class LinkedList: def __init__(self): self.head = None self.tail = None def append(self, item): node = SingleNode(item) if self.head is None: self.head = node else: self.tail.next = node self.tail = node return self def iternodes(self): current = self.head while current: yield current current = current.next ll = LinkedList() ll.append('abc') ll.append(1).append(2) ll.append('def')
print(ll.head, ll.tail)
for item in ll.iternodes(): print(item) 输出: 'abc' 'def' 'abc' 1 2 'def'
|
单向链表2
借助列表实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| class SingleNode: def __init__(self, item, next=None): self.item = item self.next = next def __repr__(self): return repr(self.item) class LinkedList: def __init__(self): self.head = None self.tail = None self.items = [] def append(self, item): node = SingleNode(item) if self.head is None: self.head = node else: self.tail.next = node
self.tail = node self.items.append(node) return self def iternodes(self): current = self.head
while current: yield current current = current.next
def getitem(self, index): return self.items[index] ll = LinkedList() ll.append('abc') ll.append(1).append(2) ll.append('def')
print(ll.head, ll.tail)
for item in ll.iternodes(): print(item) for i in range(len(ll.items)): print(ll.getitem(i)) 输出: 'abc' 'def' 'abc' 1 2 'def' 'abc' 1 2 'def'
|
为什么在单向链表中使用list?
因为只有结点自己知道下一跳是谁,想直接访问某一个结点只能遍历。
借助列表就可以方便的随机访问某一个结点了
双向链表
实现单向链表没有实现的pop
、remove
、insert
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
| class SingleNode: """ 代表一个节点,存储节点Node """ def __init__(self, val, next=None, prev=None): self.val = val self.next = next self.prev = prev def __repr__(self): return str(self.val) def __str__(self): return str(self.val)
class LinkedList: """ 容器类,按某种方式存储一个个节点 """ def __init__(self): self.head = None self.tail = None
def append(self, val): node = SingleNode(val) if self.head is None:
self.head = node else: self.tail.next = node node.prev = self.tail
self.tail = node
def iternodes(self, reverse=False): current = self.tail if reverse else self.head while current: yield current current = current.prev if reverse else current.next def pop(self): if self.tail is None: raise Exception('Empty') tail = self.tail prev = tail.prev if prev is None: self.head = None self.tail = None
else: self.tail = prev prev.next = None
return tail.val def getitem(self, index): if index < 0: return None current = None for i,node in enumerate(self.iternodes()): if i == index: current = node break if current is not None: return current
def insert(self, index, val): if index < 0: raise Exception('Error') current = None for i,node in enumerate(self.iternodes()): if i == index: current = node break
if current is None: self.append(val) return
prev = current.prev next = current.next node = SingleNode(val) if prev is None: self.head = node node.next = current current.prev = node else: node.prev = prev node.next = current current.prev = node
ll = LinkedList() node = SingleNode('abc') ll.append(node) node = SingleNode(1) ll.append(node) node = SingleNode(2) ll.append(node) node = SingleNode(3) ll.append(node) node = SingleNode(4) ll.append(node) node = SingleNode(5) ll.append(node) node = SingleNode(6) ll.append(node) node = SingleNode('def') ll.append(node)
ll.insert(6, 6) ll.insert(7, 7) ll.insert(0, '123') ll.insert(1, '456') ll.insert(30, 'abcdefg')
ll.pop() ll.pop() ll.pop() for node in ll.iternodes(True): print(node) 输出: 6 6 5 4 3 2 1 abc 456 123 ==================================================================================== class SingleNode: def __init__(self, item, prev=None, next=None): self.item = item self.next = next self.prev = prev def __repr__(self): return "({} <== {} ==> {})".format(self.prev.item if self.prev else None, self.item, self.next.item if self.next else None)
class LinkedList: def __init__(self): self.head = None self.tail = None self.size = 0 def append(self, item): node = SingleNode(item) if self.head is None: self.head = node else: self.tail.next = node node.prev = self.tail self.tail = node return self
def insert(self, index, item): if index < 0: raise IndexError('Not negative index {}'.format(index)) current = None for i, node in enumerate(self.iternodes()): if i == index: current = node break else: self.append(item) return
node = SingleNode(item) prev = current.prev next = current
if prev is None: self.head = node else: prev.next = node node.prev = prev node.next = next next.prev = node def pop(self): if self.tail is None: raise Exception('Empty') node = self.tail item = node.item prev = node.prev if prev is None: self.head = None self.tail = None else: prev.next = None self.tail = prev return item def remove(self, index):
if self.tail is None: raise Exception('Empty') if index < 0: raise IndexError('Not negative index {}'.format(index)) current = None for i, node in enumerate(self.iternodes()): if i == index: current = node break else: raise IndexError('Wrong index {}'.format(index)) prev = current.prev next = current.next if prev is None and next is None: self.head = None self.tail = None elif prev is None: self.head = next next.prev = None elif next is None: self.tail = prev prev.next = None else: prev.next = next next.prev = prev del current def iternodes(self, reverse=False): current = self.tail if reverse else self.head while current: yield current current = current.prev if reverse else current.next ll = LinkedList() ll.append('abc') ll.append(1) ll.append(2) ll.append(3) ll.append(4) ll.append(5) ll.append('def') print(ll.head, ll.tail)
for x in ll.iternodes(True): print(x) print('============')
ll.remove(6) ll.remove(5) ll.remove(0) ll.remove(1)
for x in ll.iternodes(): print(x) print('~~~~~~~~~~~~~~~~~~')
ll.insert(3, 5) ll.insert(20, 'def') ll.insert(1, 2) ll.insert(0, 'abc') for x in ll.iternodes(): print(x) 输出: (None <== abc ==> 1) (5 <== def ==> None) (5 <== def ==> None) (4 <== 5 ==> def) (3 <== 4 ==> 5) (2 <== 3 ==> 4) (1 <== 2 ==> 3) (abc <== 1 ==> 2) (None <== abc ==> 1) ========================================= (None <== 1 ==> 3) (1 <== 3 ==> 4) (3 <== 4 ==> None) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (None <== abc ==> 1) (abc <== 1 ==> 2) (1 <== 2 ==> 3) (2 <== 3 ==> 4) (3 <== 4 ==> 5) (4 <== 5 ==> def) (5 <== def ==> None)
|
笔记
Minxi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| class Document: def __init__(self, content): self.content = content def print(self): print(self.content)
class Word(Document): pass
class PrintableWord(Word): def print(self): print('Word print {}'.format(self.content))
class Pdf(Document): pass
print(PrintableWord.mro()) word = PrintableWord('test\nabc') print(word.__dict) 输出: [<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>] {'content': 'test\nabc'}
================================================================================== 装饰器 class Document: def __init__(self, content): self.content = content def print(self): print(self.content)
class Word(Document): pass
def printable(cls): cls.print = lambda self: print(self.content) return cls
@printable
class PrintableWord(Word): def __init__(self, content): self.content = content
class Pdf(Document): pass
print(PrintableWord.__dict__)
输出: =================================================================================
class PrintableMixin: def _print(self): print('~~~~~~~~~~~') print('{}'.format(self.content))
class Document: def __init__(self, content): self.content = content def print(self): print(self.content) def printable(cls): def _print(self): print('~~~~~~~~~~~') print('{}'.format(self.content)) cls.print = _print return cls
class Word(Document): pass
@printable class PrintableWord(Word): pass
class Pdf(Document): pass
class PrintablePdf(PrintableMixin, Pdf): pass
print(PrintablePdf.mro()) word = PrintablePdf('test\nabc') print(word.__dict__)
word.print()
|