Python中的抽象类和接口有什么区别?
您有时会看到以下内容:
class Abstract1:
"""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 使用接口是因为它没有多重继承。
因为Python有多重继承,你可能还会看到这样的东西
class SomeAbstraction:
pass # lots of stuff - but missing something
class Mixin1:
def something(self):
pass # one implementation
class Mixin2:
def something(self):
pass # another
class Concrete1(SomeAbstraction, Mixin1):
pass
class Concrete2(SomeAbstraction, Mixin2):
pass
这使用一种带有混合的抽象超类来创建不相交的具体子类。
Python中的抽象类和接口有什么区别?
一个对象的接口是该对象上的一组方法和属性。
在 Python 中,我们可以使用抽象基类来定义和实施接口。
使用抽象基类
例如,假设我们要使用 collections
模块中的抽象基类之一:
import collections
class MySet(collections.Set):
pass
如果我们尝试使用它,我们会得到一个 TypeError
,因为我们创建的类不支持集合的预期行为:
>>> 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__
所以我们需要至少实现__contains__
、__iter__
和__len__
。让我们使用 documentation 中的这个实现示例:
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
实现:创建一个抽象基类
我们可以通过将元类设置为 abc.ABCMeta
并在相关方法上使用 abc.abstractmethod
装饰器来创建自己的抽象基类。元类将添加修饰函数到 __abstractmethods__
属性,防止实例化,直到定义这些函数。
import abc
例如,“可言”被定义为可以用文字表达的东西。假设我们想在 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')
现在,如果我们尝试在不实现接口的情况下创建一个 effable 对象:
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
模块来调整此方法。
结论
我们已经证明,抽象基类的创建定义了 Python 中自定义对象的接口。
Python >= 2.6 有 Abstract Base Classes。
抽象基类(缩写为 ABCs)通过提供一种定义接口的方法来补充鸭子类型,而其他技术(如 hasattr())会很笨拙。 Python 为数据结构(在 collections 模块中)、数字(在 numbers 模块中)和流(在 io 模块中)提供了许多内置的 ABC。您可以使用 abc 模块创建自己的 ABC。
还有 Zope Interface 模块,它被 zope 之外的项目使用,例如 twisted。我不太熟悉它,但有一个 wiki 页面 here 可能会有所帮助。
通常,您不需要抽象类的概念或 python 中的接口(已编辑 - 有关详细信息,请参阅 S.Lott 的答案)。
用更基本的方式来解释:界面有点像一个空的松饼盘。它是一个类文件,其中包含一组没有代码的方法定义。
抽象类是同样的东西,但并非所有函数都需要为空。有些可以有代码。它不是严格为空的。
为什么要区分:Python 没有太大的实际差异,但在大型项目的规划层面上,谈论接口可能更常见,因为没有代码。尤其是当您与习惯该术语的 Java 程序员一起工作时。
Python 并没有这两个概念。
它使用 duck typing,它消除了对接口的需求(至少对于计算机来说 :-))
Python <= 2.5:基类显然存在,但没有明确的方法将方法标记为“纯虚拟”,因此该类并不是真正抽象的。
Python >= 2.6:抽象基类做 exist (http://docs.python.org/library/abc.html)。并允许您指定必须在子类中实现的方法。我不太喜欢语法,但功能就在那里。大多数时候,从“使用”客户端使用鸭子打字可能会更好。
通常,接口仅用于使用单继承类模型的语言中。在这些单继承语言中,如果任何类可以使用特定方法或方法集,则通常使用接口。同样在这些单继承语言中,抽象类用于在没有或更多方法之外定义类变量,或者利用单继承模型来限制可以使用一组方法的类的范围。
支持多重继承模型的语言倾向于只使用类或抽象基类而不是接口。由于 Python 支持多重继承,它不使用接口,您可能希望使用基类或抽象基类。
http://docs.python.org/library/abc.html
抽象类是包含一个或多个抽象方法的类。除了抽象方法,抽象类还可以有静态、类和实例方法。但是在接口的情况下,它只有抽象方法,没有其他方法。因此,继承抽象类不是强制性的,但继承接口是强制性的。
为了完整起见,我们应该提及引入 ABC 并与接口进行比较的 PEP3119,以及原始 Talin's 注释。
抽象类不是完美的接口:
属于继承层次
是可变的
但是,如果您考虑以自己的方式编写它:
def some_function(self):
raise NotImplementedError()
interface = type(
'your_interface', (object,),
{'extra_func': some_function,
'__slots__': ['extra_func', ...]
...
'__instancecheck__': your_instance_checker,
'__subclasscheck__': your_subclass_checker
...
}
)
ok, rather as a class
or as a metaclass
and fighting with python to achieve the immutable object
and doing refactoring
...
您很快就会意识到您正在发明轮子以最终实现 abc.ABCMeta
abc.ABCMeta
被提议作为缺少的界面功能的有用补充,这在像 python 这样的语言中是公平的。
当然,在编写第 3 版时能够更好地增强它,并添加新的语法和不可变接口概念......
结论:
The abc.ABCMeta IS "pythonic" interface in python
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))
是更多信息错误消息 :)