LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 216|回复: 0

模块方法表和初始化函数

[复制链接]
发表于 2024-1-15 09:56:41 | 显示全部楼层 |阅读模式

模块方法表和初始化函数
我承诺过要向大家展示如何从 Python 程序中调用 spam_system()。 首先,我们需要在“方法表”中列出它的名称和地址:

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
注意第三个参数 ( METH_VARARGS ) ,这个标志指定会使用C的调用惯例。可选值有 METH_VARARGS 、 METH_VARARGS | METH_KEYWORDS 。值 0 代表使用 PyArg_ParseTuple() 的陈旧变量。

如果单独使用 METH_VARARGS ,函数会等待Python传来tuple格式的参数,并最终使用 PyArg_ParseTuple() 进行解析。

如果应当将关键字参数传给该函数则可以在第三个字段中设置 METH_KEYWORDS 比特位。 在此情况下,C 函数应当接受第三个 PyObject * 形参,它将为一个由关键字组成的字典。 使用 PyArg_ParseTupleAndKeywords() 来将参数解析为函数。

这个方法表必须被模块定义结构所引用。

static struct PyModuleDef spammodule = {
    PyModuleDef_HEAD_INIT,
    "spam",   /* name of module */
    spam_doc, /* module documentation, may be NULL */
    -1,       /* size of per-interpreter state of the module,
                 or -1 if the module keeps state in global variables. */
    SpamMethods
};
这个结构体必须在模块的初始化函数中传递给解释器。 初始化函数必须命名为 PyInit_name(),其中 name 是模块的名称,并且应该是模块文件中定义的唯一非 static 条目:

PyMODINIT_FUNC
PyInit_spam(void)
{
    return PyModule_Create(&spammodule);
}
请注意 PyMODINIT_FUNC 将函数声明为 PyObject * 返回类型,声明了平台所要求的任何特殊链接声明,并针对于= C++ 将函数声明为 extern "C"。

当 Python 程序首次导入 spam 模块时,PyInit_spam() 将被调用。 (有关嵌入 Python 的注释参见下文。) 它将调用 PyModule_Create(),该函数会返回一个模块对象,并基于在模块定义中找到的表(一个 PyMethodDef 结构体的数组)将内置函数对象插入到新创建的模块中。 PyModule_Create() 返回一个指向它所创建的模块对象的指针。 它可能会会程度严重的特定错误而中止,或者在模块无法成功初始化时返回 NULL。 初始化函数必须将模块对象返回给其调用者,以便将其插入到 sys.modules 中。

当嵌入 Python 时,除非 PyImport_Inittab 表中有条目,否则不会自动调用 PyInit_spam() 函数。 要将模块添加到初始化表中,请使用 PyImport_AppendInittab(),可选择随后导入该模块:

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }

    /* Add a built-in module, before Py_Initialize */
    if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
        fprintf(stderr, "Error: could not extend in-built modules table\n");
        exit(1);
    }

    /* Pass argv[0] to the Python interpreter */
    Py_SetProgramName(program);

    /* Initialize the Python interpreter.  Required.
       If this step fails, it will be a fatal error. */
    Py_Initialize();

    /* Optionally import the module; alternatively,
       import can be deferred until the embedded script
       imports it. */
    PyObject *pmodule = PyImport_ImportModule("spam");
    if (!pmodule) {
        PyErr_Print();
        fprintf(stderr, "Error: could not import module 'spam'\n");
    }

    ...

    PyMem_RawFree(program);
    return 0;
}
备注 要从 sys.modules 删除实体或导入已编译模块到一个进程里的多个解释器(或使用 fork() 而没用 exec() )会在一些扩展模块上产生错误。扩展模块作者可以在初始化内部数据结构时给出警告。
更多关于模块的现实的例子包含在Python源码包的 Modules/xxmodule.c 中。这些文件可以用作你的代码模板,或者学习。脚本 modulator.py 包含在源码发行版或Windows安装中,提供了一个简单的GUI,用来声明需要实现的函数和对象,并且可以生成供填入的模板。脚本在 Tools/modulator/ 目录。查看README以了解用法。

备注 不像我们的 spam 例子, xxmodule 使用了 多阶段初始化 (Python3.5开始引入), PyInit_spam 会返回一个 PyModuleDef 结构体,然后创建的模块放到导入机制。细节参考 PEP 489 的多阶段初始化。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表