Python中抽象类与接口的区别

Difference between abstract class and interface in Python

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


有时您会看到以下内容:

1
2
3
4
5
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风格区别是不存在的。如果有人试图定义一个正式的接口,那么它也将是一个抽象类。唯一的区别是在docstring中声明的意图。

当你输入duck时,抽象和接口之间的区别是一件令人毛骨悚然的事情。

Java使用接口,因为它没有多重继承。

因为python有多个继承,所以您也可以看到类似的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的抽象超类来创建不相交的具体子类。


What is the difference between abstract class and interface in Python?

对象的接口是该对象上的一组方法和属性。

在Python中,我们可以使用抽象基类来定义和执行接口。

使用抽象基类

例如,假设我们要使用来自collections模块的一个抽象基类:

1
2
3
import collections
class MySet(collections.Set):
    pass

如果我们尝试使用它,我们会得到一个TypeError,因为我们创建的类不支持集合的预期行为:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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__属性中,在定义这些函数之前防止实例化。

1
import abc

例如,"efvable"被定义为可以用语言表达的东西。假设我们想在python 2中定义一个可使用的抽象基类:

1
2
3
4
5
class Effable(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

或者在python 3中,元类声明略有变化:

1
2
3
4
class Effable(object, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

现在,如果我们尝试在不实现接口的情况下创建一个有效的对象:

1
2
class MyEffable(Effable):
    pass

并尝试实例化它:

1
2
3
4
>>> MyEffable()
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__

听说我们还没有完成这项工作。

现在,如果我们通过提供预期的接口来遵守:

1
2
3
class MyEffable(Effable):
    def __str__(self):
        return 'expressable!'

然后,我们可以使用从抽象类派生的类的具体版本:

1
2
3
>>> me = MyEffable()
>>> print(me)
expressable!

我们还可以用它来做其他事情,比如注册已经实现了这些接口的虚拟子类,但是我认为这超出了这个问题的范围。然而,这里演示的其他方法必须使用abc模块来适应这种方法。

结论

我们已经演示了抽象基类的创建为Python中的自定义对象定义了接口。


python>=2.6具有抽象基类。

Abstract Base Classes (abbreviated
ABCs) complement duck-typing by
providing a way to define interfaces
when other techniques like hasattr()
would be clumsy. Python comes with
many builtin ABCs for data structures
(in the collections module), numbers
(in the numbers module), and streams
(in the io module). You can create
your own ABC with the abc module.

还有Zope接口模块,它被Zope外部的项目使用,比如Twisted。我对它不太熟悉,但这里有一个维基网页可能会有所帮助。

一般来说,您不需要抽象类的概念,也不需要Python中的接口(已编辑-有关详细信息,请参阅S.lott的答案)。


python实际上没有这两个概念。

它使用duck类型,这消除了对接口的需求(至少对于计算机是这样的)。

Python <=2.5:基类显然存在,但没有明确的方法将方法标记为"纯虚拟",因此该类不是真正的抽象类。

Python >=2.6:抽象基类确实存在(http://docs.python.org/library/abc.html)。并允许您指定必须在子类中实现的方法。我不太喜欢这个语法,但是这个特性是存在的。大多数时候,最好使用"使用"客户端的duck输入。


以更基本的方式解释:界面有点像一个空的松饼锅。它是一个类文件,包含一组没有代码的方法定义。

抽象类是相同的,但并非所有函数都需要为空。有些可以有代码。它不是完全空的。

为什么与众不同:在Python中没有太大的实际差异,但是在大型项目的计划级别上,讨论接口可能更常见,因为没有代码。特别是如果你和那些习惯了这个术语的Java程序员一起工作的话。


通常,接口仅在使用单继承类模型的语言中使用。在这些单一继承语言中,如果任何类都可以使用特定的方法或方法集,则通常使用接口。同样,在这些单一继承语言中,抽象类被用来除了一个或多个方法之外定义类变量,或者利用单一继承模型来限制可以使用一组方法的类的范围。

支持多继承模型的语言倾向于只使用类或抽象基类,而不使用接口。因为python支持多继承,所以它不使用接口,您希望使用基类或抽象基类。

http://docs.python.org/library/abc.html网站