Python中的抽象类 + 混入 + 多重继承

35

因此,我认为代码可能比我能用语言表达更好地解释了我正在尝试做什么,所以接下来是代码:

import abc

class foo(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def bar(self):
        pass


class bar_for_foo_mixin(object):
    def bar(self):
        print "This should satisfy the abstract method requirement"


class myfoo(foo, bar_for_foo_mixin):
    def __init__(self):
        print "myfoo __init__ called"
        self.bar()

obj = myfoo()

结果如下:
TypeError: Can't instantiate abstract class myfoo with abstract methods bar

我正在尝试让Mixin类满足抽象/接口类的要求,我错过了什么?

2个回答

31

难道继承关系应该反过来吗?在MRO中,foo当前位于bar_for_foo_mixin之前,并且报错了。使用class myfoo(bar_for_foo_mixin, foo)应该可以解决问题。

我不确定你的类设计是否正确。因为你使用一个mixin来实现bar,也许最好不要从foo派生,而只是向'foo'类注册它(即foo.register(myfoo))。但这只是我的直觉。

为了完整起见,这里是ABC的文档


2
很好,改变继承的顺序就解决了问题。 提示:为了说明问题,代码进行了简化。我的实际场景更加复杂,涉及许多潜在的mixin和myfoo对象。 - mluebke

1
我认为(在类似情况下进行测试)反转基类可以起作用:
class myfoo(bar_for_foo_mixin, foo):
    def __init__(self):
        print "myfoo __init__ called"
        self.bar()

在mro()中,它会在找到抽象版本之前先找到bar()的具体版本。不知道这是否实际上是后台发生的事情。
谢谢,Lars
PS:在Python 2.7中可行的代码(Python 3有不同的设置元类的方法)是:
class A(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def do(self):
        pass

class B(object):
    def do(self):
        print "do"

class C(B, A):
    pass

c = C()

感谢您的回答。在py27中,您的代码确实强制要求类C中存在“do()”。但是,在py3中(我只测试了py35),情况已经不再如此。在类B中,您可以将主体替换为“pass”,并且实例“c”将会被成功创建。您知道这可能是为什么吗? - Caleb Hattingh
@cjrh:你是指B的主体还是B.do的主体?如果是前者,那么这将非常奇怪,因为它会有点违背abc的目的。 - Lars
只需在“PS:代码已经运行”的下方尝试您的代码,但将B更改为class B(object):pass。您会发现实例c已成功创建。我在Python 3.6上尝试过,结果相同。 - Caleb Hattingh
1
好的,我明白发生了什么。在Python 3中,__metaclass__不再是声明元类的方式。相反,您必须在类参数中指定元类,例如:class A(metaclass=abc.ABCMeta): <etc>。然后它就像Python 2中一样工作。 - Caleb Hattingh
检查,添加到答案。 - Lars

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接