Python中的抽象类和接口有什么区别?
对象的接口是该对象上的一组方法和属性 .
在Python中,我们可以使用抽象基类来定义和实施接口 .
使用抽象基类
例如,假设我们要使用 collections 模块中的一个抽象基类:
import collections
class MySet(collections.Set):
pass
如果我们尝试使用它,我们得到 TypeError 因为我们创建的类不支持集合的预期行为:
>>> MySet()
Traceback (most recent call last):
File "", line 1, in
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
例如,“effable”被定义为可以用单词表达的东西 . 假设我们想在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')
现在,如果我们尝试在不实现接口的情况下创建一个可用的对象:
class MyEffable(Effable):
pass
并尝试实例化它:
>>> MyEffable()
Traceback (most recent call last):
File "", line 1, in
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 模块来调整此方法 .
结论
我们已经证明,Abstract Base Class的创建定义了Python中自定义对象的接口 .