面向对象语言大都支持抽象类:比如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/