Python - abc (Abstract Base Classes)

为何使用抽象基础类?

抽象基础类,是一个接口,用于检测特定的方法。它比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/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值