Python中抽象类和接口的区别

时间:2008-12-16 17:32:28

标签: python interface abstract-class

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

8 个答案:

答案 0 :(得分:573)

您有时会看到以下内容:

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中声明的意图。

当你打鸭子时,抽象和界面之间的差异就是一个分裂的东西。

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

因为Python有多重继承,你可能也会看到类似这样的东西

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

它使用一种带有mixins的抽象超类来创建不相交的具体子类。

答案 1 :(得分:161)

  

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

例如,“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 "<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中自定义对象的接口。

答案 2 :(得分:99)

Python&gt; = 2.6有Abstract Base Classes

  

抽象基类(缩写   ABCs)补充鸭子分型   提供定义接口的方法   当其他技术,如hasattr()   会很笨拙。 Python附带   许多用于数据结构的内置ABC   (在收集模块中),数字   (在数字模块中)和流   (在io模块中)。你可以创建   你自己的ABC与abc模块。

还有Zope Interface模块,由zope之外的项目使用,如twisted。我对它并不熟悉,但有一个wiki页面here可能会有所帮助。

通常,您不需要抽象类的概念,也不需要python中的接口(已编辑 - 请参阅S.Lott的答案以获取详细信息)。

答案 3 :(得分:37)

Python实际上没有任何一个概念。

它使用duck typing,这消除了对接口的需求(至少对于计算机: - ))

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

Python&gt; = 2.6: 抽象基类做existhttp://docs.python.org/library/abc.html)。并允许您指定必须在子类中实现的方法。我不太喜欢语法,但功能就在那里。大多数情况下,从'使用'客户端使用duck typing可能更好。

答案 4 :(得分:29)

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

抽象类是一回事,但并非所有函数都需要为空。有些人可以有代码。它不是严格空的。

为什么要区分: Python中没有太大的实际区别,但在大型项目的规划级别上,谈论接口可能更常见,因为没有代码。特别是如果你正在使用习惯于这个术语的Java程序员。

答案 5 :(得分:15)

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

支持多继承模型的语言倾向于仅使用类或抽象基类而不使用接口。由于Python支持多重继承,因此它不使用接口,您可能希望使用基类或抽象基类。

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

答案 6 :(得分:1)

为完整起见,我们应该提到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这样的语言就足够了。

当然,在编写版本3并添加新语法和不可变接口概念时,它能够得到更好的增强。

结论:

The abc.ABCMeta IS "pythonic" interface in python

答案 7 :(得分:0)

抽象类是包含一个或多个抽象方法的类。除抽象方法外,抽象类还可以具有静态方法,类方法和实例方法。 但是在接口的情况下,它将仅具有抽象方法,而没有其他方法。因此,继承抽象类不是强制性的,但是继承接口是强制性的。