我不知道你想在这里提取什么(嵌套扩展模块)是否OK,无论如何推荐的结构化代码方式是通过[Python 3.Docs]: Modules - Packages。
但是,我将这个(重现问题、修复问题)作为个人练习。
1。简介
列出 2 个相关页面:
环境:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q061692747]> tree /a /f
Folder PATH listing for volume SSD0-WORK
Volume serial number is AE9E-72AC
E:.
| test00.py
|
+---py2
| mod.c
|
\---py3
helper.c
mod.c
2。 Python 2
虚拟模块试图重现问题中提到的行为。
mod.c:
#include <stdio.h>
#include <Python.h>
#define MOD_NAME "mod"
#define SUBMOD_NAME "submod"
static PyObject *pMod = NULL;
static PyObject *pSubMod = NULL;
static PyMethodDef modMethods[] = {
{NULL}
};
PyMODINIT_FUNC initmod() {
if (!pMod) {
pMod = Py_InitModule(MOD_NAME, modMethods);
if (pMod) {
PyModule_AddIntConstant(pMod, "i", -69);
pSubMod = Py_InitModule(MOD_NAME "." SUBMOD_NAME, modMethods);
if (pSubMod) {
PyModule_AddStringConstant(pSubMod, "s", "dummy");
if (PyModule_AddObject(pMod, SUBMOD_NAME, pSubMod) < 0) {
Py_XDECREF(pMod);
Py_XDECREF(pSubMod);
return;
}
}
}
}
}
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q061692747\py2]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> "f:\Install\pc032\Microsoft\VisualCForPython2\2008\Microsoft\Visual C++ for Python\9.0\vcvarsall.bat" x64
Setting environment for using Microsoft Visual Studio 2008 x64 tools.
[prompt]> dir /b
mod.c
[prompt]> cl /nologo /MD /DDLL /I"c:\Install\pc064\Python\Python\02.07.17\include" mod.c /link /NOLOGO /DLL /OUT:mod.pyd /LIBPATH:"c:\Install\pc064\Python\Python\02.07.17\libs"
mod.c
Creating library mod.lib and object mod.exp
[prompt]> dir /b
mod.c
mod.exp
mod.lib
mod.obj
mod.pyd
mod.pyd.manifest
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_02.07.17_test0\Scripts\python.exe"
Python 2.7.17 (v2.7.17:c2f86d86e6, Oct 19 2019, 21:01:17) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>>
>>> [item for item in sys.modules if "mod" in item]
[]
>>> import mod
>>>
>>> [item for item in sys.modules if "mod" in item] # !!! NOTICE the contents !!!
['mod.submod', 'mod']
>>>
>>> mod
<module 'mod' from 'mod.pyd'>
>>> mod.i
-69
>>> mod.submod
<module 'mod.submod' (built-in)>
>>> mod.submod.s
'dummy'
>>>
>>> from mod.submod import s
>>> s
'dummy'
>>>
如所见,导入带有子模块的模块,在 sys.path 中添加子模块(没看过,但我 99.99% 确定这是由 Py_InitModule)
3。 Python 3
转换为 Python 3。由于这是第 1st 步骤,请将 2 条注释行视为不存在。
mod.c:
#include <stdio.h>
#include <Python.h>
//#include "helper.c"
#define MOD_NAME "mod"
#define SUBMOD_NAME "submod"
static PyObject *pMod = NULL;
static PyObject *pSubMod = NULL;
static PyMethodDef modMethods[] = {
{NULL}
};
static struct PyModuleDef modDef = {
PyModuleDef_HEAD_INIT, MOD_NAME, NULL, -1, modMethods,
};
static struct PyModuleDef subModDef = {
PyModuleDef_HEAD_INIT, MOD_NAME "." SUBMOD_NAME, NULL, -1, modMethods,
};
PyMODINIT_FUNC PyInit_mod() {
if (!pMod) {
pMod = PyModule_Create(&modDef);
if (pMod) {
PyModule_AddIntConstant(pMod, "i", -69);
pSubMod = PyModule_Create(&subModDef);
if (pSubMod) {
PyModule_AddStringConstant(pSubMod, "s", "dummy");
if (PyModule_AddObject(pMod, SUBMOD_NAME, pSubMod) < 0) {
Py_XDECREF(pMod);
Py_XDECREF(pSubMod);
return NULL;
}
//addToSysModules(MOD_NAME "." SUBMOD_NAME, pSubMod);
}
}
}
return pMod;
}
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q061692747\py3]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x64
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.23
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
[prompt]> dir /b
helper.c
mod.c
[prompt]> cl /nologo /MD /DDLL /I"c:\Install\pc064\Python\Python\03.07.06\include" mod.c /link /NOLOGO /DLL /OUT:mod.pyd /LIBPATH:"c:\Install\pc064\Python\Python\03.07.06\libs"
mod.c
Creating library mod.lib and object mod.exp
[prompt]> dir /b
helper.c
mod.c
mod.exp
mod.lib
mod.obj
mod.pyd
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe"
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>>
>>> [item for item in sys.modules if "mod" in item]
[]
>>> import mod
>>>
>>> [item for item in sys.modules if "mod" in item] # !!! NOTICE the contents !!!
['mod']
>>>
>>> mod
<module 'mod' from 'e:\\Work\\Dev\\StackOverflow\\q061692747\\py3\\mod.pyd'>
>>> mod.i
-69
>>> mod.submod
<module 'mod.submod'>
>>> mod.submod.s
'dummy'
>>>
>>> from mod.submod import s
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'mod.submod'; 'mod' is not a package
>>> ^Z
[prompt]>
正如所见,嵌套导入是不可能的。这是因为 mod.submod 不存在于 sys.modules 中。作为概括,“嵌套”扩展子模块不再可以通过包含它们的初始化函数的模块导入。唯一的选择是手动导入它们。
作为说明:我认为 Python 3 的限制是有原因的,所以下面的内容就像玩火。
从 mod.c 中删除 2 行。
helper.c:
int addToSysModules(const char *pName, PyObject *pMod) {
PyObject *pSysModules = PySys_GetObject("modules");
if (!PyDict_Check(pSysModules)) {
return -1;
}
PyObject *pKey = PyUnicode_FromString(pName);
if (!pKey) {
return -2;
}
if (PyDict_Contains(pSysModules, pKey)) {
Py_XDECREF(pKey);
return -3;
}
Py_XDECREF(pKey);
if (PyDict_SetItemString(pSysModules, pName, pMod) == -1)
{
return -4;
}
return 0;
}
输出:
[prompt]> cl /nologo /MD /DDLL /I"c:\Install\pc064\Python\Python\03.07.06\include" mod.c /link /NOLOGO /DLL /OUT:mod.pyd /LIBPATH:"c:\Install\pc064\Python\Python\03.07.06\libs"
mod.c
Creating library mod.lib and object mod.exp
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe"
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import sys
>>>
>>> [item for item in sys.modules if "mod" in item]
[]
>>> import mod
>>>
>>> [item for item in sys.modules if "mod" in item] # !!! NOTICE the contents :) !!!
['mod.submod', 'mod']
>>>
>>> from mod.submod import s
>>> s
'dummy'
>>>
4。结束语
如上所述,这似乎更像是一种解决方法。更简洁的解决方案是通过包更好地组织模块。
由于这是出于演示目的,并且为了使代码尽可能简单,我并不总是检查 Python C API 函数的返回代码。这可能导致难以发现错误(甚至崩溃)并且永远不应该这样做(尤其是在生产代码中)。
我不太确定 PyImport_ExtendInittab 效果到底是什么,因为我没有玩过它,但 [Python 3.Docs]: Importing Modules - int PyImport_ExtendInittab(struct _inittab *newtab) 声明(强调是我的):
这应该在Py_Initialize()之前调用。
因此,在我们的上下文中调用它是不可能的。
还提到了这个(旧的)讨论(不确定它是否包含相关信息,但仍然)[Python.Mail]: [Python-Dev] nested extension modules?。