11

It seems as if that when I have an abstract base class that inherits from gevent.Greenlet (which inherits from the C extension module greenlet: https://github.com/python-greenlet/greenlet) then classes that implement it do not raise any of the abc errors about unimplemented methods.

class ActorBase(gevent.Greenlet):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def foo(self):
        print "foo"

class ActorBaseTest(ActorBase):
    def bar(self):
        print "bar"

abt = ActorBaseTest()  # no errors!

If I inherit from object it fails as expected:

class ActorBase(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def foo(self):
        print "foo"

class ActorBaseTest(ActorBase):
    def bar(self):
        print "bar"

>>> abt = ActorBaseTest()
Traceback (most recent call last):
  File "/home/dw/.virtualenvs/prj/local/lib/python2.7/site-packages/IPython/core/interactiveshell.py", line 2827, in run_code
exec code_obj in self.user_global_ns, self.user_ns
  File "<ipython-input-6-d67a142e7297>", line 1, in <module>
    abt = ActorBaseTest()
TypeError: Can't instantiate abstract class ActorBaseTest with abstract methods foo

What is the right way to implement this functionality?

4

1 回答 1

7

问题的原因是它是object.__new__检查抽象类的实例化的方法,在这种情况下object.__new__没有被调用: gevent.Greenlet继承自greenlet.greenlet, 并且greenlet.greenlet是 C 扩展类型,其实现在任何时候__new__都不会调用object.__new__(请参阅C 源代码green_new中的函数)。greenlet

您可以通过继承一些其他内置类型来实现相同的效果,这些内置类型实现了自己的__new__方法并且不引用回object.__new__float例如类型)。不过,这个问题并不是 C 扩展类型所特有的:您也可以使用纯 Python 类型来复制它。考虑下面的代码:

import abc

class A(object):
    def __new__(cls):
        # self = object.__new__(cls)
        return 42

class B(A):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def foo(self):
        pass

b = B()  # No exception.

该类B已正确注册为抽象类(在内部,其Py_TPFLAGS_IS_ABSTRACT位在tp_flags字段中设置),但从未被调用,因此实例化object.__new__时没有错误。B但是,如果您取消注释中的self = object.__new__(cls)方法调用A,您将在实例化时看到预期的错误。

至于实现这一点的“正确方法”,不幸的是,我认为正确的方法是修复greenlet类型,以便其__new__方法调用object.__new__. 我想你可以添加一个__new__方法来ActorBase显式调用基类__new__ object.__new__(并丢弃后者的结果),但我认为这是一个丑陋的解决方法,而不是“正确的方法”。(编辑:最重要的是,它不起作用。我TypeError: object.__new__(ActorBase) is not safe, use greenlet.greenlet.__new__()object.__new__电话中得到。)我已经在 greenlet 跟踪器上打开了一个问题。


编辑:这个问题似乎有些熟悉,我只是深入研究了Enthought Traits源代码,它定义了一个CHasTraits用 C 实现的类,它ABC 很好地配合。它的__new__方法是这样开始的(评论来自原始来源,不是我的):

PyObject *
has_traits_new ( PyTypeObject * type, PyObject * args, PyObject * kwds ) {

    // Call PyBaseObject_Type.tp_new to do the actual construction.
    // This allows things like ABCMeta machinery to work correctly
    // which is implemented at the C level.
    has_traits_object * obj = (has_traits_object *) PyBaseObject_Type.tp_new(type, empty_tuple, empty_dict);

因此,也许长期的解决方案是说服greenlet人们做类似的事情。

于 2013-12-07T10:28:33.593 回答