Python2.6及后期版本的新特性——接口和抽象类
16lz
2021-01-22
PEP3119:抽象基类
有些面向对象的语言,如JAVA,支持接口,可以声明一个支持给定的一些方法方法,或者支持给定存取协议的类。抽象基类(或者ABCs)是Python里一个相同的特性。抽象基类由abc模块构成,包含了一个叫做ABCMeta的metaclass。这个metaclass由内置的isinstance()和issubclass()特别处理,并包含一批会被Python开发人员广泛用到的基础抽象基类。将来的Python版本可能会加入更多的抽象基类。
比如说有某个特定类你想知道它是否支持dictionary类型的存取。然而dictionary类型是个模糊的表述。它可能意味着可以通过obj[1]进行存取。那是否也意味着obj[2]=value这种赋值也起作用呢?又或者该对象将具备keys(),values()和items()方法?迭代的变种如iterkeys(),copy()和update()又如何呢?通过对象迭代的iter()呢?
Python2.6的collections模块包括了许多不同的抽象基类来表示出这些不同。Iterable表明一个类定义了__iter__(),Container意味着该类定义了__contains__()方法,因此支持xiny表达式。基本的dictionary接口包括存取数据和keys(),values(),以及items(),由MutableMapping抽象基类定义。
你可以让你的类继承某个特定的抽象基类,来表示它们支持抽象基类接口:
importcollections
classStorage(collections.MutableMapping):
...
另外,你可以不继承基类,以调用抽象基类的register()方法的方式注册该类。
importcollections
classStorage:
...
collections.MutableMapping.register(Storage)
相对于你写的类来说,从抽象基类继承可能更清晰。当你已经写了一个新的抽象基类,能描述一个存在的类型或类,或者你想声明某些第三方类实现了一个抽象基类,
register()方法是有用的。例如,如果你定义了一个PrintableType抽象基类,以下是合法的:
#RegisterPython'stypes
PrintableType.register(int)
PrintableType.register(float)
PrintableType.register(str)
类应当遵循由抽象基类指明的语义,但是Python不能检查这一点;类作者应该理解抽象基类的需求,并据此实现代码。
要检查一个对象是否支持某个特定接口,你可以这样写:
deffunc(d):
ifnotisinstance(d,collections.MutableMapping):
raiseValueError("Mappingobjectexpected,not%r"%d)
不要认为你必须像上面的例子那样,写许多检查性的代码。Python有很强的duck-typing传统,从来不会进行显式的类型检查。代码只是简单的调用对象的方法,认为这些方
法会存在,如果不存在就会抛出异常。抽象基类检查时一定要谨慎,最好在非常必要的时候才那样做。
你可以在类的定义中用abc.ABCMeta作为metaclass写自己的抽象基类:
fromabcimportABCMeta,abstractmethod
classDrawable():
__metaclass__=ABCMeta
@abstractmethod
defdraw(self,x,y,scale=1.0):
pass
defdraw_doubled(self,x,y):
self.draw(x,y,scale=2.0)
classSquare(Drawable):
defdraw(self,x,y,scale):
...
在以上的Drawable抽象基类中,draw_doubled()方法会按对象两倍的大小画出来,并且可以调用Drawable自己的方法来实现。因此实现这个抽象基类的类不需要
提供它们自己的draw_doubled()实现,尽管他们可以那样做。然而draw()的实现是必须的;抽象基类不能提供一个有用的一般实现。
你可以在必须实现的方法,如draw()中应用@abstractmethod修饰符;Python会对那些没有定义该方法的类抛出异常。注意,只有当你试图创建一个子类实例,但是却缺少该方法的时候才会抛出异常。
>>>classCircle(Drawable):
...pass
...
>>>c=Circle()
Traceback(mostrecentcalllast):
File"<stdin>",line1,in<module>
TypeError:Can'tinstantiateabstractclassCirclewithabstractmethodsdraw
>>>
抽象数据属性可以用@abstractpropertydecorator声明:
fromabcimportabstractproperty
...
@abstractproperty
defreadonly(self):
returnself._x
子类必须定义一个readonly()属性。
参考:
PEP3119-IntroducingAbstractBaseClasses
PEP由GuidovanRossum和Talin编写。由GuidovanRossum实现。由BenjaminAranguren和AlexMartelli反向移植到2.6
fromhttp://www.onlypython.com/post/3521/
有些面向对象的语言,如JAVA,支持接口,可以声明一个支持给定的一些方法方法,或者支持给定存取协议的类。抽象基类(或者ABCs)是Python里一个相同的特性。抽象基类由abc模块构成,包含了一个叫做ABCMeta的metaclass。这个metaclass由内置的isinstance()和issubclass()特别处理,并包含一批会被Python开发人员广泛用到的基础抽象基类。将来的Python版本可能会加入更多的抽象基类。
比如说有某个特定类你想知道它是否支持dictionary类型的存取。然而dictionary类型是个模糊的表述。它可能意味着可以通过obj[1]进行存取。那是否也意味着obj[2]=value这种赋值也起作用呢?又或者该对象将具备keys(),values()和items()方法?迭代的变种如iterkeys(),copy()和update()又如何呢?通过对象迭代的iter()呢?
Python2.6的collections模块包括了许多不同的抽象基类来表示出这些不同。Iterable表明一个类定义了__iter__(),Container意味着该类定义了__contains__()方法,因此支持xiny表达式。基本的dictionary接口包括存取数据和keys(),values(),以及items(),由MutableMapping抽象基类定义。
你可以让你的类继承某个特定的抽象基类,来表示它们支持抽象基类接口:
importcollections
classStorage(collections.MutableMapping):
...
另外,你可以不继承基类,以调用抽象基类的register()方法的方式注册该类。
importcollections
classStorage:
...
collections.MutableMapping.register(Storage)
相对于你写的类来说,从抽象基类继承可能更清晰。当你已经写了一个新的抽象基类,能描述一个存在的类型或类,或者你想声明某些第三方类实现了一个抽象基类,
register()方法是有用的。例如,如果你定义了一个PrintableType抽象基类,以下是合法的:
#RegisterPython'stypes
PrintableType.register(int)
PrintableType.register(float)
PrintableType.register(str)
类应当遵循由抽象基类指明的语义,但是Python不能检查这一点;类作者应该理解抽象基类的需求,并据此实现代码。
要检查一个对象是否支持某个特定接口,你可以这样写:
deffunc(d):
ifnotisinstance(d,collections.MutableMapping):
raiseValueError("Mappingobjectexpected,not%r"%d)
不要认为你必须像上面的例子那样,写许多检查性的代码。Python有很强的duck-typing传统,从来不会进行显式的类型检查。代码只是简单的调用对象的方法,认为这些方
法会存在,如果不存在就会抛出异常。抽象基类检查时一定要谨慎,最好在非常必要的时候才那样做。
你可以在类的定义中用abc.ABCMeta作为metaclass写自己的抽象基类:
fromabcimportABCMeta,abstractmethod
classDrawable():
__metaclass__=ABCMeta
@abstractmethod
defdraw(self,x,y,scale=1.0):
pass
defdraw_doubled(self,x,y):
self.draw(x,y,scale=2.0)
classSquare(Drawable):
defdraw(self,x,y,scale):
...
在以上的Drawable抽象基类中,draw_doubled()方法会按对象两倍的大小画出来,并且可以调用Drawable自己的方法来实现。因此实现这个抽象基类的类不需要
提供它们自己的draw_doubled()实现,尽管他们可以那样做。然而draw()的实现是必须的;抽象基类不能提供一个有用的一般实现。
你可以在必须实现的方法,如draw()中应用@abstractmethod修饰符;Python会对那些没有定义该方法的类抛出异常。注意,只有当你试图创建一个子类实例,但是却缺少该方法的时候才会抛出异常。
>>>classCircle(Drawable):
...pass
...
>>>c=Circle()
Traceback(mostrecentcalllast):
File"<stdin>",line1,in<module>
TypeError:Can'tinstantiateabstractclassCirclewithabstractmethodsdraw
>>>
抽象数据属性可以用@abstractpropertydecorator声明:
fromabcimportabstractproperty
...
@abstractproperty
defreadonly(self):
returnself._x
子类必须定义一个readonly()属性。
参考:
PEP3119-IntroducingAbstractBaseClasses
PEP由GuidovanRossum和Talin编写。由GuidovanRossum实现。由BenjaminAranguren和AlexMartelli反向移植到2.6
fromhttp://www.onlypython.com/post/3521/
更多相关文章
- python tkinter窗口弹出置顶的方法
- Python文件遍历的三种方法
- Django rest framework 使用自定义认证方式
- python--继承--方法的重写---和父类的扩展
- Python学习/复习神器-->各种方法/技巧在哪用和典型例子(一)
- Python:Sympy定义与包含变量的边界的积分
- Python测试函数和类 笨方法学习Python
- Python列表以及列表的处理方法
- 在python中复制命令的正确方法[复制]