Python的抽象类

Posted on | 1102 words | ~3 mins

面向对象语言大都支持抽象类:比如C#中的abstract关键字,C++的类方法的=0语法。python也支持面向对象编程范式。如何在Python中创建一个抽象类呢?答案是 ABCMeta(ABC = Abstract Base Class)。

Python中声明抽象基类

1import abc
2
3class BaseCalculator(object):
4    __metaclass__ = abc.ABCMeta
5
6    @abc.abstractmethod
7    def calculate(self, data):
8        print 'i am base'

运行下述代码,会收到TypeError异常,可见BaseCalculator是一个无法初始化的抽象类。请注意:尽管calculate有一个完整的实现(没有抛出NotImplementedError异常),它依旧是一个抽象方法。

1>>> a = BaseCalculator()
2>>> a.calculate([])
3TypeError: Can\'t instantiate abstract class BaseCalculator with abstract methods calculate

使用直接继承抽象基类,创建子类

和其他面向对象语言一样,子类继承抽象父类,并实现它的全部抽象方法即可:

1class DerivedCalculator2(BaseCalculator):
2    def calculate(self, data):
3        print 'i am dervied'

运行下述代码,则得到输出:

1>>> print 'Subclass:', issubclass(DerivedCalculator2, BaseCalculator)
2Subclass: True
3
4>>> print 'Instance:', isinstance(DerivedCalculator2(), BaseCalculator)
5Instance: True
6
7>>> a = DerivedCalculator2()
8>>> a.calculate([])
9i am derived

issubclass和isinstance帮助我们验证了DerivedCalculator2的确是BaseCalculator子类。

“注册”子类到抽象基类

除了“传统”的继承,Python中还允许“注册”一个子类到抽象基类:

1class DerivedCalculator1(object):
2    def calculate(self, data):
3        print 'i am derived'
4
5BaseCalculator.register(DerivedCalculator1)

运行下述代码,我们得到完全一样的输出:

1>>> print 'Subclass:', issubclass(DerivedCalculator1, BaseCalculator)
2Subclass: True
3
4>>> print 'Instance:', isinstance(DerivedCalculator1(), BaseCalculator)
5Instance: True
6
7>>> a = DerivedCalculator1()
8>>> a.calculate([])
9i am derived

不过如果调用抽象基类的__subclasses__,你会发现这个“注册”的子类并没有出现在subclass列表中。下面代码只输出DerivedCalculator2

1for sc in BaseCalculator.__subclasses__():
2    print sc.__name__

部分实现子类

如果BaseCalculator中有两个抽象方法calculate和report。DerviedCalculator1和DerviedCalculator2都只实现了其中的calculate,称它们为部分实现子类。

 1import abc
 2
 3class BaseCalculator(object):
 4    __metaclass__ = abc.ABCMeta
 5
 6    @abc.abstractmethod
 7    def calculate(self, data):
 8        print 'i am base'
 9
10    @abc.abstractmethod
11    def report(self):
12        print 'i am base'
13
14class DerivedCalculator1(object):
15    def calculate(self, data):
16        print 'i am derived'
17
18BaseCalculator.register(DerivedCalculator1)
19
20class DerivedCalculator2(BaseCalculator):
21    def calculate(self, data):
22        print 'i am dervied'

运行下述代码,在最后一个print处会遇到TypeError异常 。用“注册”实现的部分子类,竟然通过了isinstance的检查!而直接继承的子类则未通过检查。考虑到子类的确不是一个“完整的”抽象父类之子类,我们可不希望直到调用“report”方法时程序才崩溃!基于防御性编程思想Defensive Programming,最好能在某个“很早的时刻”,通过Python运行时检查把问题及时暴露出来。所以使用直接继承声明子类,结合instance判断,不失为更好的选择。

 1>>> print 'Subclass:', issubclass(DerivedCalculator1, BaseCalculator)
 2Subclass: True
 3
 4>>> print 'Instance:', isinstance(DerivedCalculator1(), BaseCalculator)
 5Instance: True
 6
 7>>> print 'Subclass:', issubclass(DerivedCalculator2, BaseCalculator)
 8Subclass: True
 9
10>>> print 'Instance:', isinstance(DerivedCalculator2(), BaseCalculator)
11TypeError: Can\'t instantiate abstract class DerivedCalculator2 with abstract methods report

本文所述内容参考自https://pymotw.com/2/abc/