Python类的多继承

Python2.2之前类是没有共同的祖先的,之后,引入object类,它是所有类的共同祖先object。Python2中为了兼容,分为古典类(旧式类)和新式类。Python3中全部都是新式类。

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

多继承

OCP原则:多用“继承”、少修改。

继承的用途:增强基类、实现多态。

多态

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

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

多继承弊端

多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性,带来了冲突。

如同一个孩子继承了来自父母双方的特征,那么到底眼睛像爸爸还是妈妈呢?孩子究竟该像谁多一点呢?

多继承的实现会导致编译器设计的复杂度增加,所以现在很多语言也舍弃了类的多继承。

c++支持多继承,Java舍弃了多继承。

Java中,一个类可以实现多个借口,一个借口也可以继承多个接口。Java的接口很纯粹,只是方法的声明,继承者必须实现这些方法,就具有这些能力,就能干什么。

多继承可能会带来二义性,例如,猫和狗都继承自动物类,现在如果一个类多继承了猫和狗类,猫和狗都有shout方法,子类究竟继承谁的shout呢?

解决方案

实现多继承的语言,要解决二义性,深度优先或者广度优先。

Python多继承实现

class ClassName(基类列表):
    类体

左图是多继承,右图是单一继承

多继承带来路径选择问题,究竟继承哪个父类的特征呢?

Python使用MRO(method resolution order)解决基类搜索顺序问题。

历史原因,MRO有三个搜索算法:

  1. 经典算法,按照定义从左到右,深度优先策略。2.2.之前,左图的MRO是MYclass,D,B,A,C,A
  2. 新式类算法,经典算法的升级,重复的只保留最后一个。左图的MRO是MYclass,D,B,C,A,object
  3. 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'x81xa1dx08'

用面想对象实现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)
 
 
 
 
原文地址:https://www.cnblogs.com/xpc51/p/11764975.html