Python PyCFunction_New/PyCFunction_NewEx的文档

Python PyCFunction_New/PyCFunction_NewEx的文档,python,c++,cpython,pycxx,Python,C++,Cpython,Pycxx,我正在努力理解一些围绕PyCFunction_New的代码(C++Python包装器) 有人能解释一下这个功能是如何工作的吗 (我无法从CPython上找到答案。) 在这里,我将详细说明我遇到的问题。我在上面画了一条线,因为这可能不会有如此广泛的用途 问这个问题的原因是我在处理奇怪的代码。我有一个关键字方法处理函数: static PyObject* keyword_handler( PyObject* _self_and_name_tuple,

我正在努力理解一些围绕PyCFunction_New的代码(C++Python包装器)

有人能解释一下这个功能是如何工作的吗

(我无法从CPython上找到答案。)


在这里,我将详细说明我遇到的问题。我在上面画了一条线,因为这可能不会有如此广泛的用途

问这个问题的原因是我在处理奇怪的代码。我有一个关键字方法处理函数:

    static PyObject* keyword_handler( PyObject* _self_and_name_tuple, 
                                      PyObject* _args, 
                                      PyObject* _keywords ) { }
它被存储为:

PyMethodDef meth_def_ext;
meth_def_ext.ml_meth = reinterpret_cast<PyCFunction>( _handler );
meth_def.ml_flags = METH_VARARGS | METH_KEYWORDS;
现在,他们必须通过调用以下三个函数中的适当一个,将此方法存储在查找中

    typedef Object (T::*method_noargs_function_t)();
    static void add_noargs_method( const char* name, 
                                   method_noargs_function_t function ) {
        lookup()[std::string{name}] = new MethodDefExt<T> 
                                   {name,function,noargs_handler,doc};
    }

    typedef Object (T::*method_varargs_function_t)( const Tuple& args );
    static void add_varargs_method( const char* name, 
                                    method_varargs_function_t function ) {
        lookup()[std::string{name}] = new MethodDefExt<T> 
                                    {name,function,varargs_handler,doc};
    }

    typedef Object (T::*method_keyword_function_t)( const Tuple& args, const Dict& kws );
    static void add_keyword_method( const char* name, 
                                    method_keyword_function_t function ) {
        lookup()[std::string{name}] = new MethodDefExt<T> 
                                    {name,function,keyword_handler,doc};
    }
X(见下文)&method_def_ext->meth_def拉出处理程序函数,它是三个处理程序之一 然而,多亏了MethodDefExt的构造函数,它们都被类型转换为PyCFunction对象 这意味着关键字处理程序的参数列表是错误的

        return Object(func, true);
    }
(由于SO的格式化程序没有将这些注释作为代码注释处理,因此我不得不将它们分开)

我正在努力解决的是:假设foo是一个接受关键字的函数,因此它的签名将是:

MyExt::foo(PyObject* args, PyObject* kw)
匹配的处理程序如下所示:

    static PyObject* noargs_handler( PyObject* _self_and_name_tuple, 
                                     PyObject*  ) { }

    static PyObject* varargs_handler( PyObject* _self_and_name_tuple, 
                                      PyObject* _args ) { }

    static PyObject* keyword_handler( PyObject* _self_and_name_tuple, 
                                      PyObject* _args, 
                                      PyObject* _keywords ) { }
i、 第三个。我读过Python提供了一个额外的first\u self\u和\u name\u tuple参数

当我们将foo注册到查找中时,我们提供以下处理程序:

    typedef                               Object (T::*method_keyword_function_t)( const Tuple& args, const Dict& kws );
    static void add_keyword_method( const char* name, method_keyword_function_t function ) {
        methods()[std::string{name}] = new MethodDefExt<T> {name,function,keyword_handler,doc};
    }
我们正在将处理程序输入到该文件中。这些处理程序有2个或3个参数

这看起来真的不对

然后返回,当CPython想要执行foo时,如上所述,它将获取这个meth_def.ml_meth,并将其输入到PyCFunction\u New

        Tuple args{2};

        args[0] = Object{ this };
        args[1] = Object{ PyCapsule_New( (void*)method_def_ext, nullptr, nullptr ), true };

        PyObject* func = PyCFunction_New( & method_def_ext->meth_def, args.ptr() ); // https://github.com/python/cpython/blob/master/Objects/methodobject.c#L19-L48
所以我可以猜一下: *PyCFunction_New的第一个参数必须是PyCFunction函数指针 *第二个参数必须是PyObject*\u self\u和\u name\u元组

我们正在把这个反馈给CPython 我的猜测是,当CPython想要使用“foo(7,a=1,b=2)”时,它会将7打包成args,a=1,b=2打包成kwds,并调用:

[the PyCFunction function pointer](_self_and_name_tuple, args, kwds)

我会冒险回答:

PyObject* PyCFunction_New(PyMethodDef* ml, PyObject* data)
PyCFunction_New可能会创建一个可调用类型的PyObject,用一个函数(用ml包装)和其他数据(用self包装)初始化

第二个参数可以是任何东西,事实上它甚至不需要是PyObject*。当Python执行打包在ml中的函数时,这将是第一个参数。后续参数取决于ml->ml_标志,如下所述

第一个参数是PyMethodDef对象,我们可以使用它来封装函数

struct PyMethodDef {
    const char  *ml_name;   /* The name of the built-in function/method */
    PyCFunction ml_meth;    /* The C function that implements it */
    int         ml_flags;   /* Combination of METH_xxx flags, which mostly
                               describe the args expected by the C func */
    const char  *ml_doc;    /* The __doc__ attribute, or NULL */
};
typedef struct PyMethodDef PyMethodDef;
因此,它包含一个(特定)函数指针:

typedef PyObject *(*PyCFunction)(PyObject*, PyObject*);
。。。还有一面旗帜

/* Flag passed to newmethodobject */
/* #define METH_OLDARGS  0x0000   -- unsupported now */
#define METH_VARARGS  0x0001
#define METH_KEYWORDS 0x0002
/* METH_NOARGS and METH_O must not be combined with the flags above. */
#define METH_NOARGS   0x0004
#define METH_O        0x0008

我们可以通过这种方式将3种函数传递给Python:

PyObject*foo( PyObject* data )                                 // ml_meth=METH_NOARGS
PyObject*foo( PyObject* data, PyObject* args )                 // ml_meth=METH_VARARGS
PyObject*foo( PyObject* data, PyObject* args, PyObject* kwds ) // ml_meth=METH_KEYWORDS
编辑:

如果您仍然不了解方法的工作原理,请查看 实施或许可以澄清问题。当一个实例属性 如果引用的不是数据属性,则将搜索其类。如果 名称表示一个有效的类属性,它是一个函数对象,一个 方法对象是通过打包(指向)实例对象而创建的 和函数对象一起出现在一个抽象对象中: 这是方法对象。当使用 参数列表,从实例构造一个新的参数列表 对象和参数列表,并使用 这是一个新的参数列表


第二个参数必须是Python 3.5中的
null
PyObject
(其他版本不知道)(因此任何东西都不正确),因为
PyCFunction\u New
将尝试在其上调用
Py\u XINCREF
。如果它不是这两种行为中的一种,那么它可能会导致未定义的行为。我知道这个答案很古老,但我刚刚在自己试图找出答案的时候找到了它。
        Tuple args{2};

        args[0] = Object{ this };
        args[1] = Object{ PyCapsule_New( (void*)method_def_ext, nullptr, nullptr ), true };

        PyObject* func = PyCFunction_New( & method_def_ext->meth_def, args.ptr() ); // https://github.com/python/cpython/blob/master/Objects/methodobject.c#L19-L48
[the PyCFunction function pointer](_self_and_name_tuple, args, kwds)
PyObject* PyCFunction_New(PyMethodDef* ml, PyObject* data)
struct PyMethodDef {
    const char  *ml_name;   /* The name of the built-in function/method */
    PyCFunction ml_meth;    /* The C function that implements it */
    int         ml_flags;   /* Combination of METH_xxx flags, which mostly
                               describe the args expected by the C func */
    const char  *ml_doc;    /* The __doc__ attribute, or NULL */
};
typedef struct PyMethodDef PyMethodDef;
typedef PyObject *(*PyCFunction)(PyObject*, PyObject*);
/* Flag passed to newmethodobject */
/* #define METH_OLDARGS  0x0000   -- unsupported now */
#define METH_VARARGS  0x0001
#define METH_KEYWORDS 0x0002
/* METH_NOARGS and METH_O must not be combined with the flags above. */
#define METH_NOARGS   0x0004
#define METH_O        0x0008
PyObject*foo( PyObject* data )                                 // ml_meth=METH_NOARGS
PyObject*foo( PyObject* data, PyObject* args )                 // ml_meth=METH_VARARGS
PyObject*foo( PyObject* data, PyObject* args, PyObject* kwds ) // ml_meth=METH_KEYWORDS