Why use Abstract Base Classes?
当你不太熟悉某个应用程序的源码,又想开发基于这个应用程序的plug-in extentions,你就需要了解程序中定义的抽象基类。同样的,在大型团队或大型代码库上工作时了解抽象类也很有必要,因为你很难同时track所有的类...
How ABCs Work
抽象基类和普通类的区别就是:
- 抽象基类本身会被标记为metaclass(基元类)
- 在抽象基类中一些函数会被标记为abstract method,所有继承抽象基类的子类中都需要override这些method,否则会报错(通过报错信息就可以很方便地排查你写的新类有哪些地方没实现)
将抽象基类标记成metaclass有两种方法: 注册 和 直接继承metaclass
把抽象基类的函数标记成抽象函数使用装饰器 @abc.abstractmethod
Registering a Concrete Class
下面的@PluginBase.register这个装饰器的作用就是把 RegisteredImplementation注册为一个抽象基类
import abc
from abc_base import PluginBase
class LocalBaseClass:
pass
@PluginBase.register
class RegisteredImplementation(LocalBaseClass):
def load(self, input):
return input.read()
def save(self, output, data):
return output.write(data)
if __name__ == '__main__':
print('Subclass:', issubclass(RegisteredImplementation,
PluginBase))
print('Instance:', isinstance(RegisteredImplementation(),
PluginBase))
'''
$ python3 abc_register.py
Subclass: True
Instance: True
'''
Implementation Through Subclassing
可以看多下面的PluginBase类是直接继承了ABCMeta类,这使得他也自动获得抽象基类特性.
函数load和save前面的@abc.abstractmethod装饰器则是将load和save标记为需要子类来实现的抽象函数
import abc
class PluginBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def load(self, input):
"""
Retrieve data from the input source and return an object.
you need to implement it in subclass.
"""
@abc.abstractmethod
def save(self, output, data):
"""
Save the data object to the output.
you need to implement it in subclass.
"""
class SubclassImplementation(PluginBase):
def load(self, input):
return input.read()
def save(self, output, data):
return output.write(data)
if __name__ == '__main__':
print('Subclass:', issubclass(SubclassImplementation,
PluginBase))
print('Instance:', isinstance(SubclassImplementation(),
PluginBase))
'''
$ python3 abc_subclass.py
Subclass: True
Instance: True
'''
使用继承的方法还有一个好处:可以从抽象基类中获取所有实现了抽象函数的子类
import abc
from abc_base import PluginBase
import abc_subclass
import abc_register
for sc in PluginBase.__subclasses__():
print(sc.__name__)
'''
$ python3 abc_find_subclasses.py
SubclassImplementation
'''
Incomplete Implementations
如果你在子类中没有实现抽象函数,会报错
import abc
from abc_base import PluginBase
@PluginBase.register
class IncompleteImplementation(PluginBase):
def save(self, output, data):
return output.write(data)
if __name__ == '__main__':
print('Subclass:', issubclass(IncompleteImplementation,
PluginBase))
print('Instance:', isinstance(IncompleteImplementation(),
PluginBase))
'''
$ python3 abc_incomplete.py
Subclass: True
Traceback (most recent call last):
File "abc_incomplete.py", line 24, in <module>
print('Instance:', isinstance(IncompleteImplementation(),
TypeError: Can't instantiate abstract class
IncompleteImplementation with abstract methods load
'''
Concrete Methods in ABCs
如果你在基类中也实现了抽象函数,那么在实例化子类的时候, 基类的抽象函数也会被调用(所以最好抽象基类中不要implement那些抽象函数)
import abc
import io
class ABCWithConcreteImplementation(abc.ABC):
@abc.abstractmethod
def retrieve_values(self, input):
print('base class reading data')
return input.read()
class ConcreteOverride(ABCWithConcreteImplementation):
def retrieve_values(self, input):
base_data = super(ConcreteOverride,
self).retrieve_values(input)
print('subclass sorting data')
response = sorted(base_data.splitlines())
return response
input = io.StringIO("""line one
line two
line three
""")
reader = ConcreteOverride()
print(reader.retrieve_values(input))
print()
'''
$ python3 abc_concrete_method.py
base class reading data
subclass sorting data
['line one', 'line three', 'line two']
'''
除了抽象基类外,想实现接口检查功能还可以通过NotImplementedError
class Base(object):
def go(self):
raise NotImplementedError("Please Implement this method")
class Specialized(Base):
def go(self):
print "Consider me implemented"
参考链接