gpt4 book ai didi

python - python抽象基类可以继承自C扩展吗?

转载 作者:太空狗 更新时间:2023-10-29 18:07:12 25 4
gpt4 key购买 nike

似乎当我有一个继承自 gevent.Greenlet 的抽象基类(它继承自 C 扩展模块 greenlet: https://github.com/python-greenlet/greenlet )时,实现它的类不会引发任何关于未实现的 abc 错误方法。

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 继承,它会按预期失败:

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

实现此功能的正确方法是什么?

最佳答案

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

您可以通过子类化一些实现自己的 __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 被实例化时没有错误。但是,如果您取消注释 A 中的 self = object.__new__(cls) 方法调用,您将在实例化时看到预期的错误。

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


编辑:这个问题看起来有点眼熟,我只是深入研究了 Enthought Traits source,它定义了一个用 C 语言实现的 CHasTraits 类,确实可以很好地处理 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 的人做类似的事情。

关于python - python抽象基类可以继承自C扩展吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20432335/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com