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风格的区别不存在。如果有人费心定义一个正式的接口,它也将是一个抽象类。唯一的区别在于docstring中陈述的意图。
当你使用鸭子类型时,抽象和接口之间的区别是微不足道的。
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中,我们可以使用抽象基类来定义和强制实现一个接口。
例如,假设我们想要使用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__
。让我们使用来自文档的这个实现示例:
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
class Effable(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
class Effable(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
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中创建抽象基类可以为自定义对象定义接口。
class Effable(object, metaclass=abc.ABCMeta):
和 class Effable(abc.ABC):
之间有什么区别(如果有的话)? - Donatas Olsevičius更基本的解释是: 接口有点像一个空的松饼盘。 它是一个类文件,具有一组没有代码的方法定义。
抽象类也是同样的东西,但不是所有函数都需要为空。有些可以有代码。它不是严格为空的。
为什么要区分: 在Python中实际上没有太大的区别,但对于大型项目的规划,更常见的是讨论接口,因为它没有代码。特别是如果你正在与习惯于这个术语的Java程序员一起工作。
Python并没有这两个概念。
它使用鸭子类型,这样就不需要接口(至少对于计算机而言是这样的 :-))
Python <= 2.5: 基类显然存在,但没有明确的方法可以将方法标记为“纯虚拟”,所以该类实际上并不是抽象的。
Python >= 2.6: 抽象基类确实存在 (http://docs.python.org/library/abc.html)。并且允许您指定必须在子类中实现的方法。我不太喜欢语法,但该功能已经存在。大多数时候,从“使用”客户端方面来说,使用鸭子类型可能更好。
抽象类是包含一个或多个抽象方法的类。除了抽象方法外,抽象类还可以具有静态、类和实例方法。 但在接口的情况下,它只有抽象方法而没有其他方法。因此,不必继承抽象类,但必须继承接口。
为了完整起见,我们应该提到PEP3119,其中介绍了ABC并与接口进行了比较,以及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这样的语言中这是相当公平的。The abc.ABCMeta IS "pythonic" interface in python