Python 2.6,3抽象基类误解

时间:2010-05-19 00:46:09

标签: python abstract-class

当我使用ABCMeta和abstractmethod时,我没有看到我的期望。

这在python3中运行良好:

from abc import ABCMeta, abstractmethod

class Super(metaclass=ABCMeta):
    @abstractmethod
    def method(self):
        pass

a = Super()
TypeError: Can't instantiate abstract class Super ...

在2.6:

class Super():
    __metaclass__ = ABCMeta
    @abstractmethod
    def method(self):
         pass

a = Super()
TypeError: Can't instantiate abstract class Super ...

除了ABCMeta之外,如果我从对象派生Super,它们也都可以正常工作(我得到预期的异常)。

如果我从列表中派生出Super,他们都“失败”(没有异常)。

我希望抽象基类成为列表但是抽象,并且在子类中具体。

我做错了,还是我不想在python中使用它?

2 个答案:

答案 0 :(得分:15)

使用Super构建,就像在您的工作片段中一样,当您执行Super()时,您正在调用的是:

>>> Super.__init__
<slot wrapper '__init__' of 'object' objects>

如果Super继承自list,请将其称为Superlist

>>> Superlist.__init__
<slot wrapper '__init__' of 'list' objects>

现在,抽象基类可用作 mixin 类,可以多次继承(以获得ABC可能提供的“模板方法”设计模式功能)以及具体类,没有产生后代的抽象。所以考虑一下:

>>> class Listsuper(Super, list): pass
... 
>>> Listsuper.__init__
<slot wrapper '__init__' of 'list' objects>

看到问题?根据多重继承的规则调用Listsuper()允许失败只是因为有一个悬空的抽象方法)运行与调用Superlist()相同的代码(你可以使用喜欢失败)。在实践中(list.__init__),该代码确实反对悬挂抽象方法 - 仅object.__init__。并修复可能会破坏依赖于当前行为的代码。

建议的解决方法是:如果您需要抽象基类,则其所有基础必须是抽象的。因此,不要在您的基础中使用具体的list作为基础collections.MutableSequence,添加__init__来生成._list属性,并实施MutableSequence'通过直接委派给self._list的抽象方法。不完美,但也不是那么痛苦。

答案 1 :(得分:2)

实际上,问题在于__new__,而不是__init__。例如:

from abc import ABCMeta, abstractmethod
from collections import OrderedDict

class Foo(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        return 42

class Empty:
    def __init__(self):
        pass

class C1(Empty, Foo): pass
class C2(OrderedDict, Foo): pass

C1()未按预期TypeError失败,而C2.foo()则返回42

>>> C1.__init__
<function Empty.__init__ at 0x7fa9a6c01400>

正如您所看到的,它不使用object.__init__,甚至不调用其超类(object__init__

您可以自己致电__new__来验证它:

C2.__new__(C2)工作得很好,而你通常会TypeError C1.__new__(C1)

所以,它并不像

那样明确
  

如果你想要一个抽象基类,它的所有基础都必须是抽象的。

虽然这是一个很好的建议,但反过来并不一定如此:OrderedDictEmpty都不是抽象的,而前者的子类是&# 34;具体&#34;,而后者是&#34;抽象&#34;

如果您想知道,我在示例中使用了OrderedDict而不是list,因为后者是&#34;内置&#34}。类型,因此你不能这样做:

OrderedDict.bar = lambda self: 42

我想明确表示问题与此无关。