在Python C API中从子类型传递参数到tp_new和tp_init

mlo*_*kot 9 python python-c-api

我最初在Python capi-sig列表中提出了这个问题:如何从子类型向tp_new和tp_init传递参数?

我正在阅读关于子类型的Python PEP-253,并且有很多关于如何构造类型,调用tp_newtp_init插槽等的好建议.

但是,它没有关于将参数从sub传递到super类型的重要说明.根据说明,PEP-253似乎未完成:

(XXX应该有一两段关于在这里传递的论点.)

所以,我试图推断Python类子类型中众所周知的一些策略,特别是每个级别剥离参数等的技术.

我正在寻找技术来实现类似的效果,但使用普通的Python C API(3.x):

class Shape:
    def __init__(self, shapename, **kwds):
        self.shapename = shapename
        super().__init__(**kwds)

class ColoredShape(Shape):
    def __init__(self, color, **kwds):
        self.color = color
        super().__init__(**kwds)
Run Code Online (Sandbox Code Playgroud)

Python C API中的等价物是什么?

如何处理类似的情况,但具有特定于派生类的参数以不同的顺序预期?它是在args元组结尾处给出的参数(或者kwdsdict,我假设原理是相同的).

这是一些(伪)代码,说明了这种情况:

class Base:
   def __init__(self, x, y, z):
      self.x = x
      self.y = y
      self.z = z

class Derived(Base):
   def __init__(self, x, y, a):
      self.a = a
      super().__init__(x, y, None):
Run Code Online (Sandbox Code Playgroud)

请注意,如果a预期是第一个:

Derived.__init__(self, a, x, y)
Run Code Online (Sandbox Code Playgroud)

这将是类似的情况在ShapeColoredShape上面.我想,处理起来也会更容易.

任何人都可以帮助找出上面提到的缺失的XXX评论和正确的技术,以便在构造中将参数从子类型传递到超类型吗?

更新2012-07-17:

受到ecatmur在下面的回答的启发,我查看了Python 3的源代码,我发现collections.defaultdict类型对象的defdict_init构造 函数很有趣.该类型派生自,其构造函数需要额外的参数.Python类中的构造函数签名是这样的:PyDictObjectdefault_factory

class collections.defaultdict([default_factory[, ...]])
Run Code Online (Sandbox Code Playgroud)

现在,这里是如何default_factory从原始args元组中剥离,所以其余的参数被转发到tp_init基类型,它是PyDictObject:

int result;
PyObject *newargs;
Py_ssize_t n = PyTuple_GET_SIZE(args);
...
newargs = PySequence_GetSlice(args, 1, n);
...
result = PyDict_Type.tp_init(self, newargs, kwds);
Run Code Online (Sandbox Code Playgroud)

注意,这个剪切仅存在defdict_init功能的相关部分.

eca*_*mur 5

问题是,PyArgs_ParseTupleAndKeywords没有提供一个方法来提取额外的*args**kwargs从输入指定参数和关键字; 实际上,任何额外的论证都会产生一个TypeError; "函数需要%s%d位置参数(%d给定)"或"'%U'是此函数的无效关键字参数".

这意味着您将不得不自己解析args和关键字; 你保证args是一个元组,关键字是一个dict,所以你可以使用标准方法(PyTuple_GET_ITEMPyDict_GetItemString)来提取你感兴趣的参数,并识别和构造一个元组和dict从其余部分传递.你显然无法修改args,因为元组是不可变的; 虽然从关键字弹出项目应该没问题,但它看起来确实有点危险(例如崩溃).

一个更雄心勃勃但绝对可行的路线是vgetargskeywordsgetargs.c(http://hg.python.org/cpython/file/tip/Python/getargs.c)复制并扩展它以获取余数*args和可选的out参数**kwargs.这应该是相当简单的,因为您只需要修改它检测到的部分并抛出TypeError额外的参数(额外的args ; 额外的关键字).祝你好运,如果你选择这条路线.