为何使用抽象基础类? |
抽象基础类,是一个接口,用于检测特定的方法。它比hasattr()方法更严格。定义一个基础类,你可以为子类定义一系列API。该方法很有用,尤其遇到一些第三方实现时,例如应用程序插件,同样可以避免为大工程的每个模块去实现一大片代码。 |
ABCs如何工作 |
abc 会将基类的方法作为一个抽象的对象,然后注册基本抽象类的具体实现。如果你的代码注册了一个指定API,你可以使用issubclass() 或isinstance()检测基础抽象类的对象。 首先定义一个基础类,用于实现一系列插件的API,例如保存和加载数据。 import abc class PluginBase(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def load(self, input): """Retrieve data from the input source and return an object.""" return @abc.abstractmethod def save(self, output, data): """Save the data object to the output.""" return |
注册具体类 |
现有两种方法可以实现具体类:注册类 或直接作为abc的子类。 import abc
from abc_base import PluginBase
class RegisteredImplementation(object):
def load(self, input):
return input.read()
def save(self, output, data):
return output.write(data)
PluginBase.register(RegisteredImplementation)
if __name__ == '__main__':
print 'Subclass:', issubclass(RegisteredImplementation, PluginBase)
print 'Instance:', isinstance(RegisteredImplementation(), PluginBase) 此案例中的PluginImplementation并非继承PluginBase,它被注册为PluginBaseAPI。 $ python abc_register.py
Subclass: True |
通过子类实现 |
直接作为基类的子类,我们可以不用通过上面的方法注册类。 import abc
from abc_base import PluginBase
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) Python类管理机制会将PluginImplementation作为PluginBase的具体实现。 $ python abc_subclass.py
Subclass: True
Instance: True 使用子类的副作用就是,可以询问基类,以便找到由基类派生的类(这并非abc的特性,所有类对象都这么做)。 import abc
from abc_base import PluginBase
import abc_subclass
import abc_register
for sc in PluginBase.__subclasses__():
print sc.__name__ 注意:尽管abc_register是被导入的,但RegisteredImplementation并不在子类列表中,因为它并非由基类派生. $ python abc_find_subclasses.py
SubclassImplementation Dr. André Roberge 曾经介绍过如何发现插件,通过动态导入目录下的模块,并查询已实现的子类。 |
不完整的实现 |
直接作为抽象基类的另一个好处就是,子类不需要被初始化,除非它被完全重新实现。如果只实现部分,而尝试初始化,将会在运行时出错。 import abc
from abc_base import PluginBase
class IncompleteImplementation(PluginBase):
def save(self, output, data):
return output.write(data)
PluginBase.register(IncompleteImplementation)
if __name__ == '__main__':
print 'Subclass:', issubclass(IncompleteImplementation, PluginBase)
print 'Instance:', isinstance(IncompleteImplementation(), PluginBase)
$ python abc_incomplete.py
Subclass: True
Instance:
Traceback (most recent call last):
File "abc_incomplete.py", line 22, in <module>
print 'Instance:', isinstance(IncompleteImplementation(), PluginBase)
TypeError: Can't instantiate abstract class IncompleteImplementation with abstract method |
ABCs 具体类方法 |
具体的类需要提供一个抽象方法的具体实现,抽象基类可以调用super()提供一种实现。这样就可以重用基类中的逻辑,在子类中可以定义自己的逻辑覆盖基类。 import abc
from cStringIO import StringIO
class ABCWithConcreteImplementation(object):
__metaclass__ = abc.ABCMeta
@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 = StringIO("""line one
line two
line three
""")
reader = ConcreteOverride()
print reader.retrieve_values(input)
print 由于ABCWithConcreteImplementation是一个抽象基类,它不能被直接实例化。子类必须提供retrieve_values(),本例中数据在返回前,被处理。 $ python abc_concrete_method.py
base class reading data
subclass sorting data
['line one', 'line three', 'line two'] |
抽象属性 |
如果你的API除了方法外, 还包含一些属性。你可以在基类中使用@abstractproperty定义它们,然后在具体实现的类中申明这个属性。 import abc class Base(object): __metaclass__ = abc.ABCMeta @abc.abstractproperty def value(self): return 'Should never get here' class Implementation(Base): @property def value(self): return 'concrete property' try: b = Base() print 'Base.value:', b.value except Exception, err: print 'ERROR:', str(err) i = Implementation() print 'Implementation.value:', i.value 示例中的Base类不能被初始化,因为它只有一个抽象属性getter . $ python abc_abstractproperty.py ERROR: Can't instantiate abstract class Base with abstract methods value Implementation.value: concrete property 你可以定义抽象属性 read/write . import abc class Base(object): __metaclass__ = abc.ABCMeta def value_getter(self): return 'Should never see this' def value_setter(self, newvalue): return value = abc.abstractproperty(value_getter, value_setter) class PartialImplementation(Base): @abc.abstractproperty def value(self): return 'Read-only' class Implementation(Base): _value = 'Default value' def value_getter(self): return self._value def value_setter(self, newvalue): self._value = newvalue value = property(value_getter, value_setter) try: b = Base() print 'Base.value:', b.value except Exception, err: print 'ERROR:', str(err) try: p = PartialImplementation() print 'PartialImplementation.value:', p.value except Exception, err: print 'ERROR:', str(err) i = Implementation() print 'Implementation.value:', i.value i.value = 'New value' print 'Changed value:', i.value 注意: 类的具体属性必须在抽象基类中同样的声明。如果只是尝试覆盖示例中read/write属性中一个,将不会起作用。 $ python abc_abstractproperty_rw.py ERROR: Can't instantiate abstract class Base with abstract methods value ERROR: Can't instantiate abstract class PartialImplementation with abstract methods value Implementation.value: Default value Changed value: New value 同样也可以使用修饰语法T,处理基类的read/write属性,定义的名必须相同。 import abc class Base(object): __metaclass__ = abc.ABCMeta @abc.abstractproperty def value(self): return 'Should never see this' @value.setter def value(self, newvalue): return class Implementation(Base): _value = 'Default value' @property def value(self): return self._value @value.setter def value(self, newvalue): self._value = newvalue i = Implementation() print 'Implementation.value:', i.value i.value = 'New value' print 'Changed value:', i.value Notice that both methods in the Base and Implementation classes are named value(), although they have different signatures. $ python abc_abstractproperty_rw_deco.py Implementation.value: Default value Changed value: New value |
[推荐阅读]: http://pymotw.com/2/abc/