【问题标题】:Have C extension class inherit from Python class让 C 扩展类继承自 Python 类
【发布时间】:2020-05-22 17:13:12
【问题描述】:

我有一个 Python 类 AbstractFoo,它使用 @abc.abstractmethod 定义 2 个抽象方法。

为了提高性能,大部分工作都是在 C 中作为 Python 扩展类 CFoo 实现的,所以我想做的是从 AbstractFoo 继承该扩展类。

但是我还没有找到一种方法来做到这一点并采取了不同的路线:将其实现为 C 中的“普通”类,并拥有一个继承自抽象 Python 和扩展类的class ConcreteFoo(AbstractFoo, CFoo)

但是现在 ABC 的“保护”没有发挥作用:即使缺少方法也不会引发错误。我在https://stackoverflow.com/a/20440259/1930508 中找到了一个解决方案,并添加了一个新的new 方法而不是PyType_GenericNew(暂时忽略empty_* 部分的引用计数):

static PyObject* CFoo_new(PyTypeObject* type, PyObject*, PyObject*)
{
    auto* empty_tuple(PyTuple_New(0));
    if (!empty_tuple)
        return nullptr;
    auto* empty_dict(PyDict_New());
    if (!empty_dict)
        return nullptr;
    return PyBaseObject_Type.tp_new(type, empty_tuple.get(), empty_dict.get());
}

但现在 ABC 检查总是触发:TypeError: Can't instantiate abstract class ConcreteFoo with abstract methods ...
但是检查dir(ConcreteFoo) 会显示它抱怨的方法。

有没有办法让我的AbstractFoo 的 ABC 检查子类在 C 中实现方法?

编辑:更多代码:

class AbstractFoo(abc.ABC):
    @abc.abstractmethod
    def register(self):
        pass
    # ...

#...

class ConcreteFoo(AbstractFoo, CFoo):
    def __init__(self, arg):
        AbstractFoo.__init__(self)
        CFoo.__init__(self, arg)

【问题讨论】:

  • 我认为__abstractmethods__ 是由ABCMeta 在创建类型时设置的。因此,您需要确保在定义类型ConcreteFoo 时调用此元类,而不是PyType_Type。不幸的是,您没有显示这段代码...
  • 我不确定你的意思是什么代码。未显示的代码几乎是您使用的基本样板代码。我在上面添加了ConcreteFoo的定义和AbstractFoo的重要部分
  • 啊 - 我有点误解,以为你在 C 中声明 ConcreteFoo(例如 PyObject_Call(PyType_Type,...)这确实让事情更清楚
  • register 是抽象方法名称的危险选择,因为它与 abc's own use of the name register 冲突。

标签: python python-c-api


【解决方案1】:

您必须将CFoo 设为第一个基类。这与 C-API 几乎没有关系,也适用于纯 Python 版本:如果您定义

class CFoo:
    def register(self):
        return "CFoo.register"

然后

class ConcreteFoo(AbstractFoo, CFoo):

失败,但是

class ConcreteFoo(CFoo, AbstractFoo):

有效。

这在__mro__ 方面是有意义的,您可以通过使register 不是抽象方法来测试它。您会发现优先使用第一个基类的方法。当第一个基类是AbstractFoo 时,这个首先找到的抽象方法,所以它失败了。

【讨论】:

  • 这是正确且有效的,谢谢。我发现这违反直觉,因为我期望从 lft 到右的继承工作。为了完整起见:你碰巧有官方文档的链接吗?
  • 也许是python.org/download/releases/2.3/mro(尽管它显然很快就会变得非常详细)。也许考虑它的方式是所有基类中的所有方法仍然存在,并且它从左到右搜索以找到合适的方法(而不是“方法被添加的新基类覆盖”,我认为是你的假设)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
  • 2011-06-25
  • 2013-09-26
相关资源
最近更新 更多