Python abc (Abstract Base Classes)之使用

 

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,而是將它注册为PluginBaseAPI,可如下查驗:

$ 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/

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值