abc如何工作
abc可将基类抽象化,之後再注册此抽象类的具体实现,你可以使用issubclass() 或isinstance()检测此抽象类的具体对象。
首先定义一个基础抽象类(*儲存為abc_base.py模組),例如要作保存和加载数据:
import abc
class PluginBase(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def load(self, input):
"""Retrieve data from the input source and return an object."""
raise NotImplemented
@abc.abstractmethod
def save(self, output, data):
"""Save the data object to the output."""
raise NotImplemented
注册具体类
有两种方法可來实现具体类:注册类或直接作为abc的子类,底下是注冊类之作法(*存為 abc_register.py)。
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)
此案例中的RegisteredImplementation并非继承PluginBase,而是將它注册为PluginBase之API,可如下查驗:
$ python abc_register.py
Subclass: True
Instance: True
通过繼承子类实现
直接作为抽象类的子类,此方式我们可以不用通过上面的方法來注册类:
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类管理机制会将SubclassImplementation直接作为PluginBase的具体子類之实现(*存為abc_subclass.py),可以如下查驗:
$ 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 曾经介绍过如何发现插件來查驗通过动态导入目录下的模块,并查询其下已实现的子类。
不完整的实现
作抽象基类的好处就是如果子类只实现部分,而尝试初始化将会在运行时出错(*存為abc_incomplete.py):
import abc
from abc_base import PluginBase
class IncompleteImplementation(PluginBase): #load()接口未實現
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()调用抽象类之方法,这样就可重用抽象类中的逻辑,而在子类中定义自己的逻辑來覆盖抽象类(*存為abc_concrete_method.py)。(*注:此概念與Java有些不同,可強調具体子类必需實現抽象类之接口,且抽象类不可被實例化)
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()) # splitlines()去除Enter
return response
input = StringIO("""line one
line two
line three
""")
reader = ConcreteOverride()
print reader.retrieve_values(input)
由于ABCWithConcreteImplementation是一个抽象类,故它不能被直接实例化,子类必须實現retrieve_values()方法,本例中数据在返回前是由抽象类去处理前段步驟。
$ python abc_concrete_method.py
base class reading data
subclass sorting data
['line one', 'line three', 'line two']
抽象属性
如果抽象类除了方法外,还包含一些@属性,你可以在基类中使用@abstractproperty來定义它们,然后在具体实现子类中要申明这个属性(*存為abc_abstractproperty.py)。
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,但定義之名稱須不同(*存為abc_abstractproperty_rw.py)。
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
同样也可以採用直接修饰语法來处理基类的read/write属性,但定义的名稱必须相同(*存為abc_abstractproperty_rw_deco.py)。
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/