当我使用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中使用它?
答案 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)
所以,它并不像
那样明确如果你想要一个抽象基类,它的所有基础都必须是抽象的。
虽然这是一个很好的建议,但反过来并不一定如此:OrderedDict
和Empty
都不是抽象的,而前者的子类是&# 34;具体&#34;,而后者是&#34;抽象&#34;
如果您想知道,我在示例中使用了OrderedDict
而不是list
,因为后者是&#34;内置&#34}。类型,因此你不能这样做:
OrderedDict.bar = lambda self: 42
我想明确表示问题与此无关。