python抽象基类可以从C扩展继承吗? [英] Can python abstract base classes inherit from C extensions?
问题描述
当我有一个继承自gevent.Greenlet的抽象基类时(似乎继承自C扩展模块greenlet: https://github.com/python-greenlet/greenlet ),则实现该类的类不会引发任何有关未实现方法的abc错误.
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!
如果我从 object
继承,它会按预期失败:
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?
推荐答案
问题的原因是它是 greenlet
C源代码中的 green_new
函数.
The cause of your problem is that it's the object.__new__
method that does the check for instantiation of an abstract class, and in this case object.__new__
isn't being invoked: gevent.Greenlet
inherits from greenlet.greenlet
, and greenlet.greenlet
is a C extension type whose __new__
implementation doesn't call object.__new__
at any point (see the green_new
function in the greenlet
C source).
通过将实现其自己的 __ new __
方法的其他一些内置类型的子类化,可以看到相同的效果,而不必回头引用 object .__ new __
(<例如code> float 类型).但是,该问题并不只限于C扩展类型:您还可以使用纯Python类型来复制它.考虑下面的代码:
You can see the same effect by subclassing some of the other builtin types that implement their own __new__
method and don't refer back to object.__new__
(the float
type, for example). The issue is not particular to C extension types, though: you can also replicate it with pure Python types. Consider the code below:
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
时不会出错.但是,如果取消注释 A
中的 self = object .__ new __(cls)
方法调用,则会在实例化时看到预期的错误.
The class B
is correctly registered as an abstract class (internally, its Py_TPFLAGS_IS_ABSTRACT
bit is set in the tp_flags
field), but object.__new__
is never called, so there's no error when B
is instantiated. However, if you uncomment the self = object.__new__(cls)
method call in A
, you'll see the expected error on instantiation.
就实现此目标的正确方法"而言,不幸的是,我认为正确的方法是修复 greenlet
类型,以便其 __ new __
方法调用对象.__new __
.我猜您可以在 ActorBase
中添加 __ new __
方法,以显式调用基类 __ new __
和 object .__ new __
(并丢弃后者的结果),但我认为这是一个丑陋的解决方法,而不是正确的方法".(最重要的是,它不起作用.我得到 TypeError:object .__ new __(ActorBase)不安全,请从
.__ new __ 调用.)我已经打开了 issue 绿色跟踪器.
As far as the 'right way' to implement this, unfortunately I think the right way is to fix the greenlet
type so that its __new__
method calls object.__new__
. I guess you could add a __new__
method to ActorBase
that explicitly calls both the base class __new__
and object.__new__
(and throws away the result of the latter), but I'd consider that an ugly workaround rather than the 'right way'. ( And on top of that, it doesn't work. I get TypeError: object.__new__(ActorBase) is not safe, use greenlet.greenlet.__new__()
from the object.__new__
call.) I've opened an issue on the greenlet tracker.
这个问题似乎有点耳熟,我只是对思想特质来源进行了一些研究,定义了一个用C实现的 CHasTraits
类,该类确实可以很好地与ABC一起使用.它的 __ new __
方法是这样开始的(注释来自原始来源,而不是我的):
This problem seemed somewhat familiar, and I just did some digging into the Enthought Traits source, which defines a CHasTraits
class implemented in C which does play nicely with ABCs. And its __new__
method starts like this (comments are from the original source, not mine):
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
员工做类似的事情.
So perhaps the long-term solution is to persuade the greenlet
folks to do something similar.
这篇关于python抽象基类可以从C扩展继承吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!