Python中抽象类与接口的区别

2020-12-02 20:01:50 发布

您现在位置:Python中文网/ 问答频道 /正文
3条回答
网友
1楼 ·

你有时会看到以下情况:

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有多个继承,所以您还可能看到如下内容

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的抽象超类来创建不相交的具体子类。

网友
2楼 ·

Python>;=2.6有Abstract Base Classes

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 Interface模块,它被zope以外的项目使用,比如twisted。我不是很熟悉,但是有一个wiki页面here可能会有帮助。

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

网友
3楼 ·

What is the difference between abstract class and interface in 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')

现在,如果我们尝试在不实现接口的情况下创建一个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中自定义对象的接口。

相关问题