Python中抽象类和接口的区别

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


有时你会看到以下内容:

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没有(也不需要)正式的Interface接口,所以抽象和接口之间的Java风格的区别并不存在。 如果有人经过努力来定义一个正式的界面,它也将是一个抽象类。 唯一的区别在于文档中陈述的意图。

抽象和界面之间的区别是当你有鸭子打字时的一件令人头晕的事情。

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

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


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____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方法装饰器来创建我们自己的Abstract Base Class。 元类将把装饰后的函数添加到__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中的自定义对象定义了接口。


Python> = 2.6有抽象基类。

抽象基类(缩写为ABCs)通过提供一种定义接口的方法来补充鸭式打字,而其他技术(如hasattr())则笨拙。 Python为数据结构(在集合模块中),数字(在数字模块中)和流(在io模块中)提供了许多内建ABCs。 您可以使用abc模块创建您自己的ABC。

还有Zope接口模块,它被zope之外的项目使用,如扭曲。 我并不十分熟悉它,但这里有一个wiki页面可能会有所帮助。

一般来说,你不需要python中的抽象类或接口的概念(编辑 - 详见S.Lott的答案)。

链接地址: http://www.djcxy.com/p/9199.html

上一篇: Difference between abstract class and interface in Python

下一篇: How do you declare an interface in C++?