python面对对象之继承

继承

继承的概念

  在python中,当类之间的差别比较大时,或其中一个类是另一个类的组件时(如Point类Square类的关系),组合就完全可以胜任。但当要设计的新类有相同功能却又有些扩展时,这时候就需要继承了。新建的类称为子类或派生类(Subclass), 被继承的类被称为父类、基类、超类(Base class、Super class)。

继承的作用

  子类继承父类后,就继承了父类的全部功能,而且自己可以扩展功能,覆盖重写父类已有功能,大大提高了代码的重用率。

单继承

  单继承很好理解,即只继承单一父类。子类直接调用父类的功能

class Planet(object):
    """创建一个行星类"""
    def __init__(self, name):
        self.name = name

    def rotation(self):
        print('%s在自转' % self.name)


class Earth(Planet):
    """创建一个地球类"""
    def __init__(self, name, star):
        super().__init__(name)   # 初始化要调用super()的初始化方法
        self.star = star

    def revolution(self):
        print('%s正在绕%s公转' % (self.name, self.star))


earth = Earth('地球', '太阳')
earth.rotation()
earth.revolution()
View Code
地球在自转
地球正在绕太阳公转
out

 多继承

  多继承是子类同时继承多个父类。

  查看继承的方法

>>> class A:
...     pass
... 
>>> class B:
...     pass
... 
>>> class C(A, B):
...     pass
... 
>>> A.__base__()  # 默认继承object类
<object object at 0x7f1cb33460a0>
>>> C.__base__()  # 只能查看第一个继承的父类
<__main__.A object at 0x7f1cb1641e10>
>>> C.__bases__  # 查看直接继承的所有类
(<class '__main__.A'>, <class '__main__.B'>)

多继承的问题

  在编程过程中,应该尽量避免使用多继承,多继承增加了可选方案和系统的复杂度,如会增加潜在的命名冲突问题,就如下面的"菱形问题"又称为"钻石继承问题"。python的解决方法是它内部会按照一定顺序遍历继承图,确定调用哪个方法。这个顺序叫作方法顺序解析顺序(Method Resolution Order, MRO)。在python通过__mro__或mro()方法查看。

                      

 

# 钻石继承问题
class A(object):
    def ping(self):
        print('ping', self)


class B(A):
    def pong(self):
        print('pong', self)


class C(A):
    def pong(self):
        print('PONG', self)


class D(B, C):
    def ping(self):
        print('pingpong', self)

    def pingpong(self):
        self.ping()             # 优先寻找自己的ping方法
        super().ping()          # super按照__mro__ or mro()方法列表的顺序寻找方法,从B->C->A寻找, 此处应该寻找到B的方法
        self.pong()             # 从自己的继承树寻找, 以自己为节点开始寻找
        super().pong()          # 从自己的直系第一个父类B开始寻找pong方法
        C.pong(self)            # 直接调用C类的pong方法,并把D自己作为显式参数传进去


d = D()
d.pingpong()
print(D.__mro__)
pingpong <__main__.D object at 0x7f5246967978>
ping <__main__.D object at 0x7f5246967978>
pong <__main__.D object at 0x7f5246967978>
pong <__main__.D object at 0x7f5246967978>
PONG <__main__.D object at 0x7f5246967978>
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
out

 六边形问题,以F为节点,找寻顺序如上右图所示。

# 六边形问题
class A(object):
    def run(self):
        print('A is running')


class B(A):
    # def run(self):
    #     print('B is running')
    pass


class C(A):
    def run(self):
        print('C is running')


class D(B):
    # def run(self):
    #     print('D is running')
    pass


class E(C):
    def run(self):
        print('E is running')


class F(D, E):
    # def run(self):
    #     print('F is running')
    pass

f = F()
f.run()
View Code
E is running
out

抽象类 

  抽象类特征:抽象类必须通过继承的方式才能使用,抽象类本身无法被实例化,抽象类中的子类必须实现抽象方法。

  抽象类是对类的抽象,主要定义了基本类和基础方法,它可以不实现任何方法(当然也可以实现,子类可以通过super()调用),不实现任何方法的抽象类就类似Java里的接口,可以规范子类的编程,也为代码提供了逻辑和解耦能力。以下例子来自流畅的python第十一章。

 1 import abc
 2 import random
 3 
 4 
 5 # class Container(metaclass=abc.ABCMeta):     # 此处可以也可以如此实现抽象类
 6 class Container(abc.ABC):
 7     """容器类"""
 8 
 9     @abc.abstractmethod
10     def load(self, iterable):
11         """从一个可迭代对象添加元素"""
12 
13     @abc.abstractmethod
14     def pick(self):
15         """删除一个元素并返回值"""
16 
17     def has_element(self):
18         """抽象类具体方法, 至少有一个元素,返回真"""
19         return bool(self.get_elements())
20 
21     def get_elements(self):
22         """返回所有元素, 放在一个列表中"""
23         items = []
24         while 1:
25             try:
26                 items.append(self.pick())
27             except LookupError:
28                 break
29         self.load(items)
30         return items
31 
32 
33 # 创建Container的子类
34 class NumbersBalls(Container):
35     """创建一个数字球类, 可以模拟不放回的抽奖一类的问题"""
36     def __init__(self, iterable):
37         self._balls = list(iterable)                       # 对象内部不能直接引用外部传来的可迭代对象,要自己单独维护一份
38 
39     def load(self, iterable):
40         self._balls.extend(iterable)
41 
42     def pick(self):
43         """返回随机的一个元素"""
44         try:
45             position = random.randrange(len(self._balls))  # 如果元素为空,会报ValueError异常
46         except ValueError:
47             raise LookupError('当前元素为空')
48         return self._balls.pop(position)
49 
50     def has_element(self):
51         """重写抽象类的方法,太费力了"""
52         return bool(self._balls)                           # 判断元素是否为空委托给列表的内部方法
53 
54     def get_elements(self):
55         """重写父类方法"""
56         return tuple(sorted(self._balls))                  # 返回一般也不能直接返回对象维护的数据,要重新创建一份,防止外部修改
57 
58 
59 balls = NumbersBalls(range(10))
60 print(balls.pick())
61 print(balls.pick())
62 print(balls.get_elements())
View Code
1
8
(0, 2, 3, 4, 5, 6, 7, 9)
out

 虚拟子类

  虚拟子类是不用继承抽象基类,即不会从抽象基类获得任何属性和方法,但是可以被isinstance和issubclass识别的类。注册虚拟子类的方法是调用抽象类的register方法或是在register方法作为类装饰器装饰子类。python内部是使用register方法注册虚拟子类的,如Sequece.register(tuple)等等。

 1 # class Container(metaclass=abc.ABCMeta):     # 此处可以也可以如此实现抽象类
 2 class Container(abc.ABC):
 3     """容器类"""
 4 
 5     @abc.abstractmethod
 6     def load(self, iterable):
 7         """从一个可迭代对象添加元素"""
 8 
 9     @abc.abstractmethod
10     def pick(self):
11         """删除一个元素并返回值"""
12 
13     def has_element(self):
14         """抽象类具体方法, 至少有一个元素,返回真"""
15         return bool(self.get_elements())
16 
17     def get_elements(self):
18         """返回所有元素, 放在一个列表中"""
19         items = []
20         while 1:
21             try:
22                 items.append(self.pick())
23             except LookupError:
24                 break
25         self.load(items)
26         return items
27 
28 
29 @Container.register             # 注册为Container的虚拟子类
30 class BallList(list):
31 
32     load = list.extend          # 列表的方法
33 
34     def pick(self):
35         if self:
36             return self.pop(random.randrange(len(self)))
37         else:
38             raise LookupError('列表为空')
39 
40     def get_elements(self):     # 获取所有元素
41         return tuple(self)
42 
43     def has_element(self):      # 判断是否有值
44         return bool(self)
45 
46 
47 # Container.register(BallList)  # 虚拟子类也可以如此注册
48 print(issubclass(BallList, Container))
49 print(isinstance(BallList(), Container))
50 b = BallList(range(10))
51 print(b.pick())
52 print(b.get_elements())
53 print(BallList.mro())           # 虚拟子类不在mro表中, 表明不是继承
View Code
True
True
0
(1, 2, 3, 4, 5, 6, 7, 8, 9)
[<class '__main__.BallList'>, <class 'list'>, <class 'object'>]
out

 鸭子类型

  "鸭子类型"并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。python作为一种动态语言很崇尚鸭子类型,像java里接口(虽然python没有interface这个关键字,但是python也有抽象基类这种类似接口的替代品)这种严格要求子类规范的代码运用其实不算多。python里处处遵循的是协议,它是文档定义和约定形成,属于一种非正式的接口,所以它也没有正式接口和抽象基类的强制规定约束力。

  遵循协议有很大的灵活性,可以部分实现协议,就可以利用协议带来的好处,例如最常见的序列协议。 如下面的例子,自定义的类只是实现了__getitem__和__len__方法,立刻可以调用内置函数len()和对对象进行切片。

class MyList(object):
    """没什么用,只是为了实现序列协议"""
    def __init__(self, iterator):
        self.lst = list(iterator)

    def __getitem__(self, pos):
        return self.lst[pos]

    def __len__(self):
        return len(self.lst)


mylst = MyList(range(10))
print(len(mylst))
print(mylst[::-1])

 

posted @ 2018-11-26 23:47  yscl  阅读(101)  评论(0)    收藏  举报