Python0803-多继承

Python不同版本的类

  • Python2.2之前类是没有共同的祖先的,之后引入object类,它是所有类的共同祖先类object。
  • Python2中为了兼容,分为古典类(旧式类)和新式类。
  • Python3中全部都是新式类。
  • 新式类都是继承自object的,新式类可以使用super。
# 以下代码在python2.x中运行
# 古典类(旧式类)
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呢?
  • 解决方案
    • 实现多继承的浯言,要解决二义性0深度优先或者广度优先。

Python多继承实现

class ClassName(基类列表):
    类体
digraph G {
    Myclass -> D
    Myclass -> C
    D -> B
    C -> A
    B -> A
}
digraph G {
    Myclass -> D
    D -> C
    C -> B
    B -> A
}
  • 上图是多继承,下图是单一继承
  • 多继承带来路径选择问题,究竟继承哪个父类的特征呢
  • Python使用MRO(method esolution order)解决基类搜索顺序问题
  • 历史原因,MRO有三个搜索算法:
    • 经典算法,按照定义从左到右,深度优先策。2.2之前上图的MRO是MyClass,D,B,A,C,A
    • 新式类算法,经典算法的升级,重复的只保留最后一个。2.2上图的MRO是MyClass,D,B,C,A,object
    • C3算法,在类被创建出来的时候,就计算出一个MRO有序列表。2.3之后,Python30唯一支持的算法
      • 左图中的MROEMyCIass,D,B,C,A,object的列表
      • C3算法解决多继承的二义性

多继承的缺点

  • 当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径。
  • python语法去是允许多继承,但python代码是解释执行,只有执行到的时候,才发现错误。
  • 团队协作开发,如果引入多继承,那代码将不可控。
  • 不管编程语言是否支持多继承,都应当避免多继承。
  • Python的面向对象,我们看到的太灵活了,太开放了,所以要团队守规矩。

Mixin(重要)

  • 类有下面的继承关系
digraph G {
    Word -> Document
    Pdf -> Document
}
  • 文档Document子类提供打印能力
  • Word, Pdf类是Document子类提供打印能力

思路:

在Document中提供print方法
class Document:
    def __int__(self, content):
        self.content = content


    def print(self):
        raise NotImplementedError()


class Word(Document): pass
class Pdf(Document): pass
  • 基类提供的方法不应该具体实现,因为它未必适合子类的打印,子类中需要盖重写。
  • print是一种能力——打印功能,不是所有的Document的子类都需要的,所以,从这个角度出发,有点问题。
需要打印的子类上增加
  • 如果在现有子类上直接增加,违反了OCP的原则,所以应该继承后增加。因此有下图
digraph G {
    PrintableWord -> Word
    PrintablePdf -> Pdf
    Word -> Document
    Pdf -> Document
}
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()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{'__module__': '__main__', '__doc__': None}
[<class '__main__.PrintableWord'>, <class '__main__.Printable'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
test string
  • 看似不错,如果需要还要提供其他能力,如何继承?
  • 应用于网络,文档应该具备序列化的能力,类上就应该实现序列化。
  • 可序列化还可能分为使用pickle、json、messagepack等.。
  • 这个时候发现,类可能太多了,继承的方式不是很好了·
  • 功能太多,A类需要某几样功能,B类需要另几样功能,很繁琐。
装饰器
  • 用装饰器增强一个类,把功能给类附加上去,那个类需要,就装饰它
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{'__doc__': None, '__module__': '__main__', 'print': <function printable.<locals>._print at 0x00000290683E19D8>}
[<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
test string 装饰器
  • 优点:
  • 简单方便,在需要的地方动态增加,直接使用装饰器
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(1, PrintableWord.__dict__)
print(2, PrintableWord.mro())

def printable(cls):
    def _print(self):
        print(self.content, '装饰器')
    cls.print = _print
    return cls


@printable
class PrintablePdf(Word): pass
print(3, PrintablePdf.__dict__)
print(4, PrintablePdf.mro())
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 {'__module__': '__main__', '__doc__': None}
2 [<class '__main__.PrintableWord'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
3 {'print': <function printable.<locals>._print at 0x000001C50E361AE8>, '__module__': '__main__', '__doc__': None}
4 [<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(1, PrintableWord.__dict__)
print(2, 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(3, SuperPrintablePdf.__dict__)
print(4, SuperPrintablePdf.mro())


spp = SuperPrintablePdf('super print pdf')
spp.print()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 {'__doc__': None, '__module__': '__main__'}
2 [<class '__main__.PrintableWord'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
test string Mixin
3 {'__doc__': None, '__module__': '__main__'}
4 [<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类的方式。

练习

Shape基类,要求所有子类都必须提供面积的计算,子类有三角形、矩形、圆

上题圆类的数据可序列化

作业


  • 用面向对象实现LinkList链表
    • 单向链表实现append, iternodes方法
    • 双向链表实现append, pop, insert, remove, iternoders方法

Single Linked List
digraph G {
    HEAD -> 5
    5 -> 7
    7 -> 3
    3 -> 4
    4 -> NULL
}

Double Linked List

digraph G {
    HEAD -> 5
    5 -> 7
    7 -> 3
    3 -> 4
    4 -> NULL
    4 -> 3
    3 -> 7
    7 -> 5
    5 -> NULL2
}

作业参考

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):
        a, b, c = self.a, self.b, self.c
        p = (a + b + c) / 2
        return math.sqrt(p * (p - a) * (p - b) * (p - c))


class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height


    @property
    def area(self):
        width, height = self.width, self.height
        return width * height


class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
        # self.d = redius * 2


    @property
    def area(self):
        radius = self.radius
        return math.pi * math.pow(radius, 2)
        # 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))

上题圆类的数据可序列化

import json
# import msgpack


class SerizlizableMixin:
    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(SerizlizableMixin, Circle):
    pass


scm = SerializableCircleMixin(4)
print(scm.area)
s = scm.dumps('json')
print(s)
~~~~~~~~~~~~~~~~~~~~~~~
50.26548245743669
{"radius": 4}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值