Python2.2之前类是没有共同的祖先的,之后,引入object类,它是所有类的共同祖先object。Python2中为了兼容,分为古典类(旧式类)和新式类。Python3中全部都是新式类。
新式类都是继承自object的,新式类可以使用super。
多继承
OCP原则:多用“继承”、少修改。
继承的用途:增强基类、实现多态。
多态
在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同表现,就是多态。
一个类继承自多个类就是多继承,它将具有多个类的特征。
多继承弊端
多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性,带来了冲突。
如同一个孩子继承了来自父母双方的特征,那么到底眼睛像爸爸还是妈妈呢?孩子究竟该像谁多一点呢?
多继承的实现会导致编译器设计的复杂度增加,所以现在很多语言也舍弃了类的多继承。
c++支持多继承,Java舍弃了多继承。
Java中,一个类可以实现多个借口,一个借口也可以继承多个接口。Java的接口很纯粹,只是方法的声明,继承者必须实现这些方法,就具有这些能力,就能干什么。
多继承可能会带来二义性,例如,猫和狗都继承自动物类,现在如果一个类多继承了猫和狗类,猫和狗都有shout方法,子类究竟继承谁的shout呢?
解决方案
实现多继承的语言,要解决二义性,深度优先或者广度优先。
Python多继承实现
class ClassName(基类列表):
类体
左图是多继承,右图是单一继承
多继承带来路径选择问题,究竟继承哪个父类的特征呢?
Python使用MRO(method resolution order)解决基类搜索顺序问题。
历史原因,MRO有三个搜索算法:
- 经典算法,按照定义从左到右,深度优先策略。2.2.之前,左图的MRO是MYclass,D,B,A,C,A
- 新式类算法,经典算法的升级,重复的只保留最后一个。左图的MRO是MYclass,D,B,C,A,object
- C3算法,在类被创建出来的时候,就计算出来一个MRO有序列表,2.3.之后,Python唯一支持算法。左图中的MRO是MYCLASS,d,b,c,aobject的列表。
c3算法解决了多继承的二义性。
多继承的缺点
当类很多,继承复杂的情况下, 继承路径太多,很难说清什么样的继承路径。Python语法是允许多继承,但Python代码是解释执行,只有执行到的时候,才发现错误。
团队协助开发,如果引入多继承,那代码就不可控。
不管编程语言是否支持多继承,都应当避免多继承。
Python的面向对象,我们看到的太灵活了,太开放了,所以要团队守规矩。
mixin(重要)
类有下面的继承关系
文档document类是其他所有文档类的抽象基类。Word,pdf是document的子类。
需求:为document子类提供打印能力。
思路:
1在document中提供print方法。
class Document:
def __init__(self,content):
self.content = content
def print(self):
raise NotImlementedError()
class Word(Document):pass
class Pdf(Document):pass
基类提供的方法不应该具体实现,因为它未必适合子类的打印,子类中需要覆盖重写。
print算是一种能力——打印能力,不是所有的document的子类都需要的,所以,从这个角度出发,有点问题。
2 需要打印的子类上增加
如果在现有子类上直接增加,违反了OCP原则,所以应该继承后增加,因此有下图。
class Printabe():
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(Printabe,Word):pass
print( PrintableWord.__dict__)
print( PrintableWord.mro())
pw = PrintableWord("test string")
pw.print()
结果为:
{'__module__': '__main__', '__doc__': None}
[<class '__main__.PrintableWord'>, <class '__main__.Printabe'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
test string
看似不错,如果需要还要提供其他能力,如何继承?
应用于网络,文档应该具备序列化的能力,类上就应该实现序列化。
可序列化还可能分为使用pickle,json,messagepack等。
这个时候发现,类可以太多了,继承的方式不是很好了。
功能太多,A类需要某几样功能,B类需要另几样功能,很繁琐。
3装饰器
用装饰器增强一个类,把功能给类附加上去,那个类需要,就装饰它。
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 0x0000000005A79620>}
[<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
test string 装饰器
优点:简单方便,在需要的地方动态的增加,直接使用装饰器
4.mixin
先看代码
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())
结果为:
{'__module__': '__main__', '__doc__': None}
[<class '__main__.PrintableWord'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
{'__module__': '__main__', '__doc__': None, 'print': <function printable.<locals>._print at 0x0000000005A79400>}
[<class '__main__.PrintablePdf'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
Mixin就是其他类混合进来,同时带来了类的属性和方法。
这里看Mixin类和装饰器效果一样,也没有什么特别的,但是Mixin是类,就可以继承。
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)#打印增强
#PrintableMixin类的继承
class SuperPrintablePdf(SuperPrintableMixin,Pdf):pass
print(SuperPrintablePdf.__dict__)
print(SuperPrintablePdf.mro())
spp = SuperPrintablePdf("super print pdf")
spp.print()
结果为:
{'__module__': '__main__', '__doc__': None}
[<class '__main__.PrintableWord'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
test string Mixin
{'__module__': '__main__', '__doc__': None}
[<class '__main__.SuperPrintablePdf'>, <class '__main__.SuperPrintableMixin'>, <class '__main__.PrintableMixin'>, <class '__main__.Pdf'>, <class '__main__.Document'>, <class 'object'>]
~~~~~~~~~~~~~~~~~~~~
super print pdf Mixin
~~~~~~~~~~~~~~~~~~~~
mixin类
mixin本质上就是多继承实现的,mixin体现的是一种组合的设计模式。
在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起。
从设计模式的角度来说,多组合,少继承。
mixin类的原则
- mixin类中不应该显示的出现__init__初始化方法。
- mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现。
- mixin类的祖先类也应是mixin类。
使用时,mixin类通常在继承列表的第一个位置,例如class PrintableWord(PrintableMixin,Word):pass
mixin类和装饰器
这两种方式都可以使用,看个人爱好。如果需要继承就得使用Mixin类的方式。
练习1,shape基类,要求所有子类都必须提供面积的计算,子类有三角形,矩形,圆。
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 = 50.26548245743669
2.上题圆类的数据可序列化。
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 NotImlementedError("没有实现的序列化")
class SerializableCircleMixin(SerializableMixin,Circle):
pass
scm = SerializableCircleMixin(4)
print(scm.area)
s = scm.dumps("msgpack")
print(s)
结果为:
50.26548245743669
b'\x81\xa1d\x08'
用面想对象实现LinkedList链表。
单向链表实现append,iternodes方法。
双向链表实现append,pop,insert,remove,iternodes方法。
对于链表来说,每一个结点是一个独立的对象,结点自己知道内容是什么,下一跳是什么。而链表则是一个容器,它内部装着一个个结点对象。
所以,建议设计2个类,一个是结点Node类,一个是链表LinkedList类。
单向链表
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#思考tail属性的作用
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)
单向链表2
借助列表实现
class SingleNode:
def __init__(self,item,next = None):
self.item = item
self.next = next
def __repr__(self):
return repr(self.item)
class SingleNode:#结点保存内容和下一跳
def __init__(self,item,next = None):
self.item = None
self.next = next
def __repr__(self):
return repr(self.item)
class LinkedList():
def __init__(self):
self.head = None
self.tail = None #思考tail的属性
self.items = [] #为什么在单向链表中使用list,因为对于不需要插入的链表来说,检索方便。
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))
class SingleNode():
""" 代表一个节点"""
def __init__(self,val,next = None):
self.val = val
self.next = next
def __repr__(self):
return repr(self.val)
class LinkedList():
"""容器类,某种方式存储一个个节点"""
def __init__(self):
self.nodes = []#不需要插入的列表的来说,检索方便,但是插入,remove不合适
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
def iternodes(self):
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
为什么在单向链表中使用list?
因为只有结点自己知道下一跳是谁,想直接访问某一个结点只能遍历。
借助列表就可以方便的随机访问某一个结点了。
双向链表
实现单向链表没有实现的pop,remove,insert方法。
class SingleNode:#结点保存内容和下一跳
def __init__(self,item,prev = None,next = None):
self.item = item
self.next = next
self.prev = prev#增加上一跳
def __repr__(self):
#return repr(self.item)
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 #思考tail的属性
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: #没有break,尾部追加
self.append(item)
return
#break,找到了
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:#only one node
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:#not found
raise IndexError("wrong index {}".format(index))
prev = current.prev
next = current.next
#4种情况
if prev is None and next is None:#only one node
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)
class SingleNode():
""" 代表一个节点"""
def __init__(self,val,next = None,prev = None):
self.val = val
self.next = next
self.prev = prev
def __repr__(self):
return repr(self.val)
class LinkedList():
"""容器类,某种方式存储一个个节点"""
def __init__(self):
#self.nodes = []#不需要插入的列表的来说,检索方便,但是插入,remove不合适
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
#next = tail.next#这一句在pop里面用不到
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 in
ll = LinkedList()
node = SingleNode(5)
ll.append(node)
node = SingleNode(7)
ll.append(node)
node = SingleNode("abc")
ll.append(node)
for node in ll.iternodes():
print(node)
for node in ll.iternodes(True):
print(node)
ll.pop()
ll.pop()
for node in ll.iternodes():
print(node)
class SingleNode():
""" 代表一个节点"""
def __init__(self,val,next = None,prev = None):
self.val = val
self.next = next
self.prev = prev
def __repr__(self):
return repr(self.val)
class LinkedList():
"""容器类,某种方式存储一个个节点"""
def __init__(self):
#self.nodes = []#不需要插入的列表的来说,检索方便,但是插入,remove不合适
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
#next = tail.next#这一句在pop里面用不到
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
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
prev.next = node
ll = LinkedList()
node = SingleNode(5)
ll.append(node)
node = SingleNode(7)
ll.append(node)
node = SingleNode("abc")
ll.append(node)
for node in ll.iternodes():
print(node)
for node in ll.iternodes(True):
print(node)
ll.pop()
ll.pop()
for node in ll.iternodes():
print(node)
ll.insert(6,6)
ll.insert(7,7)
for node in ll.iternodes():
print(node)