🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 导航 - [索引](../genindex.xhtml "总目录") - [模块](../py-modindex.xhtml "Python 模块索引") | - [下一页](curses.xhtml "Curses Programming with Python") | - [上一页](pyporting.xhtml "将 Python 2 代码迁移到 Python 3") | - ![](https://box.kancloud.cn/a721fc7ec672275e257bbbfde49a4d4e_16x16.png) - [Python](https://www.python.org/) » - zh\_CN 3.7.3 [文档](../index.xhtml) » - [Python 常用指引](index.xhtml) » - $('.inline-search').show(0); | # 将扩展模块移植到 Python 3 作者Benjamin Peterson 摘要 尽管更改 C-API 并不是 Python 3 的目标之一,但许多 Python 级别的更改使得 Python 2 的 API 无法完整实现。实际上,一些变化如 [`int()`](../library/functions.xhtml#int "int") 和 `long()` 的统一在 C 级别更明显。本文档致力于记录不兼容性以及如何解决这些问题。 ## 条件编译 仅编译 Python 3 的一些代码的最简单方法是检查 `PY_MAJOR_VERSION` 是否大于或等于3。 ``` #if PY_MAJOR_VERSION >= 3 #define IS_PY3K #endif ``` 不存在的 API 函数可以在条件块中别名为它们的等价物。 ## 对象API的更改 Python 3 将一些具有类似功能的类型合并在一起,同时干净地分离了其他类型。 ### str/unicode 统一 Python 3 的 [`str()`](../library/stdtypes.xhtml#str "str") 类型相当于 Python 2 的 `unicode()` ; C函数被称为 `PyUnicode_*` 。旧的 8 位字符串类型变为 [`bytes()`](../library/stdtypes.xhtml#bytes "bytes") ,其中 C 函数称为 `PyBytes_*` 。 Python 2.6 及更高版本提供了一个兼容性头文件 `bytesobject.h` ,将 `PyBytes` 名称映射到 `PyString` 。 为了保持与 Python 3 的最佳兼容性, `PyUnicode` 应该用于文本数据,并且 `PyBytes` 用于二进制数据。同样重要的是要记住 `pyBytes` 和 Python 3中的 `PyUnicode` 不可互换,如 `PyString` 和 `PyUnicode` 在 Python 2 以下中示例显示了以下方面的最佳实践 `PyUnicode` 、 `PyString` 和 `PyBytes` 。: ``` #include "stdlib.h" #include "Python.h" #include "bytesobject.h" /* text example */ static PyObject * say_hello(PyObject *self, PyObject *args) { PyObject *name, *result; if (!PyArg_ParseTuple(args, "U:say_hello", &name)) return NULL; result = PyUnicode_FromFormat("Hello, %S!", name); return result; } /* just a forward */ static char * do_encode(PyObject *); /* bytes example */ static PyObject * encode_object(PyObject *self, PyObject *args) { char *encoded; PyObject *result, *myobj; if (!PyArg_ParseTuple(args, "O:encode_object", &myobj)) return NULL; encoded = do_encode(myobj); if (encoded == NULL) return NULL; result = PyBytes_FromString(encoded); free(encoded); return result; } ``` ### long/int 统一 Python 3 只有一个整数类型, [`int()`](../library/functions.xhtml#int "int") 。但它实际上对应于Python 2 `long()` 类型 —— 删除了 Python 2 中使用的 [`int()`](../library/functions.xhtml#int "int") 类型。在 C-API 中, `PyInt_*` 函数被它们等价的 `PyLong_*` 替换。 ## 模块初始化和状态 Python 3 有一个改进的扩展模块初始化系统。(参见 [**PEP 3121**](https://www.python.org/dev/peps/pep-3121) \[https://www.python.org/dev/peps/pep-3121\] 。)而不是将模块状态存储在全局变量中,它们应该存储在特定于解释器的结构中。创建在 Python 2 和 Python 3 中正确运行的模块非常棘手。以下简单示例演示了如何操作。: ``` #include "Python.h" struct module_state { PyObject *error; }; #if PY_MAJOR_VERSION >= 3 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) #else #define GETSTATE(m) (&_state) static struct module_state _state; #endif static PyObject * error_out(PyObject *m) { struct module_state *st = GETSTATE(m); PyErr_SetString(st->error, "something bad happened"); return NULL; } static PyMethodDef myextension_methods[] = { {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, {NULL, NULL} }; #if PY_MAJOR_VERSION >= 3 static int myextension_traverse(PyObject *m, visitproc visit, void *arg) { Py_VISIT(GETSTATE(m)->error); return 0; } static int myextension_clear(PyObject *m) { Py_CLEAR(GETSTATE(m)->error); return 0; } static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "myextension", NULL, sizeof(struct module_state), myextension_methods, NULL, myextension_traverse, myextension_clear, NULL }; #define INITERROR return NULL PyMODINIT_FUNC PyInit_myextension(void) #else #define INITERROR return void initmyextension(void) #endif { #if PY_MAJOR_VERSION >= 3 PyObject *module = PyModule_Create(&moduledef); #else PyObject *module = Py_InitModule("myextension", myextension_methods); #endif if (module == NULL) INITERROR; struct module_state *st = GETSTATE(module); st->error = PyErr_NewException("myextension.Error", NULL, NULL); if (st->error == NULL) { Py_DECREF(module); INITERROR; } #if PY_MAJOR_VERSION >= 3 return module; #endif } ``` ## CObject 替换为 Capsule `Capsule` 对象是在 Python 3.1 和 2.7 中引入的,用于替换 `CObject` 。 CObject 是有用的,但是 `CObject` API 是有问题的:它不允许区分有效的 CObject ,这导致不匹配的 CObject 使解释器崩溃,并且它的一些 API 依赖于 C 中的未定义行为。有关 Capsule 背后的基本原理的进一步阅读,请参阅 [bpo-5630](https://bugs.python.org/issue5630) \[https://bugs.python.org/issue5630\] 。) 如果你当前正在使用 CObject ,并且想要迁移到 3.1 或更高版本,则需要切换到 Capsules 。 `CObject` 在 3.1 和 2.7 中已弃用,在 Python 3.2 中已完全删除。如果你只支持 2.7 或 3.1 及以上,你可以简单地切换到 `Capsule` 。如果你需要支持 Python 3.0 或早于 2.7 的 Python 版本,则必须同时支持 CObject 和 Capsule 。(请注意,不再支持 Python 3.0 ,不建议将其用于生产用途。) 以下示例头文件 `capsulethunk.h` 可以为你解决问题。只需针对 `Capsule` API 编写代码,并在以下文件后包含此头文件 `Python.h` 。你的代码将自动在带有 Capsule 的 Python 版本中使用 Capsules ,并在 Capsule 不可用时切换到 CObjects 。 `capsulethunk.h` 使用 CObject 模拟 Capsules 。 但是, `CObject` 没有提供存储胶囊的“名称”的地方。因此,模拟 `Capsule` 对象由 `capsulethunk.h` 创建,其行为与真实 Capsule 略有不同。特别地: > - 传递给 [`PyCapsule_New()`](../c-api/capsule.xhtml#c.PyCapsule_New "PyCapsule_New") 的 name 参数被忽略。 > - 传入以下命令的 name 参数 [`PyCapsule_IsValid()`](../c-api/capsule.xhtml#c.PyCapsule_IsValid "PyCapsule_IsValid") 和 [`PyCapsule_GetPointer()`](../c-api/capsule.xhtml#c.PyCapsule_GetPointer "PyCapsule_GetPointer") 被忽略,并且不执行错误检查。 > - [`PyCapsule_GetName()`](../c-api/capsule.xhtml#c.PyCapsule_GetName "PyCapsule_GetName") 总是返回 NULL 。 > - [`PyCapsule_SetName()`](../c-api/capsule.xhtml#c.PyCapsule_SetName "PyCapsule_SetName") 总是引发异常并返回失败。(由于无法在 CObject 中存储名称,因此 [`PyCapsule_SetName()`](../c-api/capsule.xhtml#c.PyCapsule_SetName "PyCapsule_SetName") 的明显失败被认为优于静默失败。如果这样不方便,请随意根据需要修改本地副本。) 你可以在 Python 源代码分发中的 [Doc/includes/capsulethunk.h](https://github.com/python/cpython/tree/3.7/Doc/includes/capsulethunk.h) \[https://github.com/python/cpython/tree/3.7/Doc/includes/capsulethunk.h\] 找到 `capsulethunk.h` 。为方便起见,我们还将其包含在此处: ``` #ifndef __CAPSULETHUNK_H #define __CAPSULETHUNK_H #if ( (PY_VERSION_HEX < 0x02070000) \ || ((PY_VERSION_HEX >= 0x03000000) \ && (PY_VERSION_HEX < 0x03010000)) ) #define __PyCapsule_GetField(capsule, field, default_value) \ ( PyCapsule_CheckExact(capsule) \ ? (((PyCObject *)capsule)->field) \ : (default_value) \ ) \ #define __PyCapsule_SetField(capsule, field, value) \ ( PyCapsule_CheckExact(capsule) \ ? (((PyCObject *)capsule)->field = value), 1 \ : 0 \ ) \ #define PyCapsule_Type PyCObject_Type #define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule)) #define PyCapsule_IsValid(capsule, name) (PyCObject_Check(capsule)) #define PyCapsule_New(pointer, name, destructor) \ (PyCObject_FromVoidPtr(pointer, destructor)) #define PyCapsule_GetPointer(capsule, name) \ (PyCObject_AsVoidPtr(capsule)) /* Don't call PyCObject_SetPointer here, it fails if there's a destructor */ #define PyCapsule_SetPointer(capsule, pointer) \ __PyCapsule_SetField(capsule, cobject, pointer) #define PyCapsule_GetDestructor(capsule) \ __PyCapsule_GetField(capsule, destructor) #define PyCapsule_SetDestructor(capsule, dtor) \ __PyCapsule_SetField(capsule, destructor, dtor) /* * Sorry, there's simply no place * to store a Capsule "name" in a CObject. */ #define PyCapsule_GetName(capsule) NULL static int PyCapsule_SetName(PyObject *capsule, const char *unused) { unused = unused; PyErr_SetString(PyExc_NotImplementedError, "can't use PyCapsule_SetName with CObjects"); return 1; } #define PyCapsule_GetContext(capsule) \ __PyCapsule_GetField(capsule, descr) #define PyCapsule_SetContext(capsule, context) \ __PyCapsule_SetField(capsule, descr, context) static void * PyCapsule_Import(const char *name, int no_block) { PyObject *object = NULL; void *return_value = NULL; char *trace; size_t name_length = (strlen(name) + 1) * sizeof(char); char *name_dup = (char *)PyMem_MALLOC(name_length); if (!name_dup) { return NULL; } memcpy(name_dup, name, name_length); trace = name_dup; while (trace) { char *dot = strchr(trace, '.'); if (dot) { *dot++ = '\0'; } if (object == NULL) { if (no_block) { object = PyImport_ImportModuleNoBlock(trace); } else { object = PyImport_ImportModule(trace); if (!object) { PyErr_Format(PyExc_ImportError, "PyCapsule_Import could not " "import module \"%s\"", trace); } } } else { PyObject *object2 = PyObject_GetAttrString(object, trace); Py_DECREF(object); object = object2; } if (!object) { goto EXIT; } trace = dot; } if (PyCObject_Check(object)) { PyCObject *cobject = (PyCObject *)object; return_value = cobject->cobject; } else { PyErr_Format(PyExc_AttributeError, "PyCapsule_Import \"%s\" is not valid", name); } EXIT: Py_XDECREF(object); if (name_dup) { PyMem_FREE(name_dup); } return return_value; } #endif /* #if PY_VERSION_HEX < 0x02070000 */ #endif /* __CAPSULETHUNK_H */ ``` ## 其他选项 如果你正在编写新的扩展模块,你可能会考虑 [Cython](http://cython.org/) \[http://cython.org/\] 。 它将类似 Python 的语言转换为 C 。它创建的扩展模块与 Python 3 和 Python 2 兼容。 ### 导航 - [索引](../genindex.xhtml "总目录") - [模块](../py-modindex.xhtml "Python 模块索引") | - [下一页](curses.xhtml "Curses Programming with Python") | - [上一页](pyporting.xhtml "将 Python 2 代码迁移到 Python 3") | - ![](https://box.kancloud.cn/a721fc7ec672275e257bbbfde49a4d4e_16x16.png) - [Python](https://www.python.org/) » - zh\_CN 3.7.3 [文档](../index.xhtml) » - [Python 常用指引](index.xhtml) » - $('.inline-search').show(0); | © [版权所有](../copyright.xhtml) 2001-2019, Python Software Foundation. Python 软件基金会是一个非盈利组织。 [请捐助。](https://www.python.org/psf/donations/) 最后更新于 5月 21, 2019. [发现了问题](../bugs.xhtml)? 使用[Sphinx](http://sphinx.pocoo.org/)1.8.4 创建。