Python中的抽象类和接口有什么区别?


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__。让我们使用文档中的这个实现示例:

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 中自定义对象的接口。


原文链接:https://codingdict.com/