Python中抽象类和接口的区别
Python中的抽象类和接口有什么区别?
有时你会看到以下内容:
class Abstract1( object ): """Some description that tells you it's abstract, often listing the methods you're expected to supply.""" def aMethod( self ): raise NotImplementedError( "Should have implemented this" )
因为Python没有(也不需要)正式的接口契约,抽象和接口之间的Java风格的区别是不存在的。 如果有人经过努力来定义一个正式的界面,它也将是一个抽象类。 唯一的区别是在文档中陈述的意图。
抽象和界面之间的区别是当你有鸭子打字的时候的一个令人毛骨悚然的事情。
Java使用接口,因为它没有多重inheritance。
因为Python有多重inheritance,所以你也可以看到类似的东西
class SomeAbstraction( object ): pass # lots of stuff - but missing something class Mixin1( object ): def something( self ): pass # one implementation class Mixin2( object ): def something( self ): pass # another class Concrete1( SomeAbstraction, Mixin1 ): pass class Concrete2( SomeAbstraction, Mixin2 ): pass
这使用一种抽象的超类与mixin来创build不相交的具体子类。
Python> = 2.6有抽象基类 。
抽象基类(缩写ABCs)通过提供一种定义接口的方法来补充鸭式input,而其他技术(如hasattr())则笨拙。 Python为数据结构(在集合模块中),数字(在数字模块中)和stream(在io模块中)提供了许多内置ABCs。 您可以使用abc模块创build您自己的ABC。
还有Zope接口模块,它被zope之外的项目使用,如扭曲。 我不太熟悉它,但是这里有一个wiki页面可能会有所帮助。
一般来说,你不需要python中的抽象类或接口的概念(编辑 – 详见S.Lott的答案)。
Python中的抽象类和接口有什么区别?
对象的接口是该对象上的一组方法和属性。
在Python中,我们可以使用抽象基类来定义和实施一个接口。
使用抽象基类
例如,假设我们想使用collections
模块中的抽象基类之一:
import collections class MySet(collections.Set): pass
如果我们尝试使用它,我们得到一个TypeError
因为我们创build的类不支持预期的行为:
>>> MySet() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class MySet with abstract methods __contains__, __iter__, __len__
所以我们需要至less实现__contains__
__iter__
, __iter__
和__len__
。 我们来从文档中使用这个实现的例子:
class ListBasedSet(collections.Set): """Alternate set implementation favoring space over speed and not requiring the set elements to be hashable. """ def __init__(self, iterable): self.elements = lst = [] for value in iterable: if value not in lst: lst.append(value) def __iter__(self): return iter(self.elements) def __contains__(self, value): return value in self.elements def __len__(self): return len(self.elements) s1 = ListBasedSet('abcdef') s2 = ListBasedSet('defghi') overlap = s1 & s2
实现:创build一个抽象基类
我们可以通过将元类设置为abc.ABCMeta
并在相关方法中使用abc.abstractmethod
方法修饰器来创build我们自己的Abstract Base Class。 元类将被添加到__abstractmethods__
属性的装饰函数,防止实例化,直到定义。
import abc
例如,“effable”被定义为可以用文字expression的东西。 假设我们想要在Python 2中定义一个可释放的抽象基类:
class Effable(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def __str__(self): raise NotImplementedError('users must define __str__ to use this base class')
或者在Python 3中,随着元类声明的轻微改变:
class Effable(object, metaclass=abc.ABCMeta): @abc.abstractmethod def __str__(self): raise NotImplementedError('users must define __str__ to use this base class')
现在,如果我们尝试创build一个没有实现接口的有效对象:
class MyEffable(Effable): pass
并尝试实例化它:
>>> MyEffable() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__
我们被告知,我们还没有完成这项工作。
现在,如果我们遵守提供预期的接口:
class MyEffable(Effable): def __str__(self): return 'expressable!'
那么我们就可以使用派生自抽象的类的具体版本:
>>> me = MyEffable() >>> print(me) expressable!
还有其他的东西,我们可以做到这一点,如已经实现这些接口的注册虚拟子类,但我认为这是超出了这个问题的范围。 然而,这里展示的其他方法将不得不使用abc
模块来适应这种方法。
结论
我们已经certificate了创build一个抽象基类在Python中定义了一个接口,因此它们是一个一样的。
Python并没有真正的概念。
它使用鸭子打字,这消除了接口的需要(至less对于电脑:-))
Python <= 2.5:基类显然存在,但没有明确的方法将方法标记为“纯虚拟”,所以类不是真正的抽象。
Python> = 2.6:抽象基类确实存在 ( http://docs.python.org/library/abc.html )。 并允许您指定必须在子类中实现的方法。 我不太喜欢语法,但function在那里。 大多数情况下,使用“使用”客户端的鸭子打字可能会更好。
在一个更基本的解释方式:一个接口有点像一个空的松饼锅。 这是一个包含一组没有代码的方法定义的类文件。
抽象类是相同的东西,但并不是所有的function都是空的。 有些可以有代码。 这不是严格的空白。
为什么区分:Python中没有太多实际的区别,但是在大型项目的规划层面上,谈论接口可能更常见,因为没有代码。 特别是如果您正在与熟悉该术语的Java程序员合作。
通常,接口仅用于使用单inheritance类模型的语言。 在这些单一inheritance语言中,如果任何类可以使用特定方法或方法集,则通常使用接口。 同样在这些单inheritance语言中,除了没有一个或多个方法,抽象类被用来定义类variables,或者利用单inheritance模型来限制可以使用一组方法的类的范围。
支持多inheritance模型的语言倾向于只使用类或抽象基类而不使用接口。 由于Python支持多重inheritance,因此它不使用接口,并且您希望使用基类或抽象基类。