从Python C API中的子类型向tp_new和tp_init传递参数 [英] Passing arguments to tp_new and tp_init from subtypes in Python C API

查看:222
本文介绍了从Python C API中的子类型向tp_new和tp_init传递参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最初在Python capi-sig列表上问了这个问题:

I originally asked this question on the Python capi-sig list: How to pass arguments to tp_new and tp_init from subtypes?

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

I'm reading the Python PEP-253 on subtyping and there are plenty of good recommendations on how to structure the types, call tp_new and tp_init slots, etc.

但是,它在将参数从sub传递到超类型方面缺乏重要的说明. 根据说明, PEP-253 尚未完成:

But, it lacks an important note on passing arguments from sub to super type. It seems the PEP-253 is unfinished as per the note:

(XXX关于此处传递的论证应该有一个或两个段落.)

(XXX There should be a paragraph or two about argument passing here.)

因此,我正在尝试推断一些策略众所周知来自Python类子类型,特别是每个级别剥离参数的技术,等等.

So, I'm trying to extrapolate some strategies well known from the Python classes subtyping, especially techniques that each level strips-off arguments, etc.

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

I'm looking for techniques to achieve similar effect to this, but using plain 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)

Python C API相当于什么?

What would be the equivalent in Python C API?

如何处理相似的情况,但要以不同的顺序处理特定于派生类的参数? 它是在args元组末尾给出的参数(或kwds dict,我认为原理是相同的.)

How to deal with similar situation but with arguments specific to derived class expected in different order? It is arguments given at the end of the args tuple (or kwds dict, I assume principle would be same).

以下是一些说明情况的(伪)代码:

Here is some (pseudo-)code that illustrates the situation:

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):

请注意,如果首先要使用a:

Note, if the a was expected first:

Derived.__init__(self, a, x, y)

与上面的ShapeColoredShape类似. 我认为这也将更容易处理.

it would be similar situation to the Shape and ColoredShape above. It would also be easier to deal with, I assume.

有人能帮助找出上面提到的缺少 XXX 的注释,以及在构造上将参数从子类型传递到超类型的正确方法吗?

Could anyone help to figure out the missing XXX comment mentioned above and correct technique for passing arguments from subtype up to super type(s) on construction?

2012年7月17日更新:

UPDATE 2012-07-17:

下面的ecatmur答案的启发,我浏览了Python 3的源代码,发现了defdict_init构造函数. "http://docs.python.org/py3k/library/collections.html#collections.defaultdict" rel ="nofollow noreferrer"> collections.defaultdict 类型的对象很有趣.该类型是从PyDictObject派生的,其构造函数使用default_factory的附加参数. Python类中的构造函数签名是这样的:

Inspired by ecatmur's answer below I looked through Python 3 sources and I found defdict_init constructor of collections.defaultdict type object interesting. The type is derived from PyDictObject and its constructor takes additional argument of default_factory. The constructor signature in Python class is this:

class collections.defaultdict([default_factory[, ...]])

现在,这是从原始args元组中剥离default_factory的方式,因此其余参数将转发到基本类型的tp_init,即PyDictObject:

Now, here is how the default_factory is stripped from original args tuple, so the rest of arguments is forwarded to the tp_init of base type, it is 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);

请注意,此片段仅显示defdict_init函数的相关部分.

Note, this snipped presence only the relevant part of the defdict_init function.

推荐答案

问题是PyArgs_ParseTupleAndKeywords没有提供从输入的args和关键字中提取额外的*args**kwargs的方法;实际上,任何多余的参数都会导致TypeError; 函数接受%s%d个位置参数(已给出%d)",或'%U'是该函数的无效关键字参数".

The problem is that PyArgs_ParseTupleAndKeywords doesn't provide a way to extract extra *args and **kwargs from the input args and keywords; indeed, any extra arguments result in a TypeError; "Function takes %s %d positional arguments (%d given)", or "'%U' is an invalid keyword argument for this function".

这意味着您将不得不自己解析args和关键字;您可以确保args是元组,而关键字是dict,因此您可以使用标准方法(PyTuple_GET_ITEMPyDict_GetItemString)提取您感兴趣的参数,并识别和构造一个元组和dict以从其余的传递.您显然不能修改args,因为元组是不可变的.并且从关键字弹出项目应该可以,但似乎有点冒险(示例崩溃).

This means that you're going to have to parse args and keywords yourself; you're guaranteed that args is a tuple and keywords is a dict, so you can use the standard methods (PyTuple_GET_ITEM and PyDict_GetItemString) to extract the arguments you're interested in, and identify and construct a tuple and dict to pass on from the remainder. You obviously can't modify args, because tuples are immutable; and while popping items from keywords should be OK it does seem a little risky (example crash).

更雄心勃勃但绝对可行的方法是从getargs.c复制vgetargskeywords( http://hg.python.org/cpython/file/tip/Python/getargs.c ),并将其扩展为对其余的*args**kwargs采用可选的参数.这应该非常简单,因为您只需要修改它检测到的部分并将TypeError抛出额外的参数(

A more ambitious but definitely feasible route would be to copy vgetargskeywords from getargs.c (http://hg.python.org/cpython/file/tip/Python/getargs.c) and extend it to take optional out-parameters for remainder *args and **kwargs. This should be fairly straightforward as you just need to modify the parts where it detects and throws TypeError on extra arguments (extra args; extra keywords). Good luck if you choose this route.

这篇关于从Python C API中的子类型向tp_new和tp_init传递参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆