Документация Python неофициальный перевод

extending.md

375 строк · 32.5 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/2.7/faq/extending.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [FAQ по расширению и встраиванию](https://python-all.ru/2.7/faq/extending.html#id2)89Содержание1011- [FAQ по расширению и встраиванию](https://python-all.ru/2.7/faq/extending.html#extending-embedding-faq)1213  - [Можно ли создавать собственные функции на C?](https://python-all.ru/2.7/faq/extending.html#can-i-create-my-own-functions-in-c)14  - [Можно ли создавать собственные функции на C++?](https://python-all.ru/2.7/faq/extending.html#id1)15  - [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/2.7/faq/extending.html#writing-c-is-hard-are-there-any-alternatives)16  - [Как выполнить произвольные инструкции Python из C?](https://python-all.ru/2.7/faq/extending.html#how-can-i-execute-arbitrary-python-statements-from-c)17  - [Как вычислить произвольное выражение Python из C?](https://python-all.ru/2.7/faq/extending.html#how-can-i-evaluate-an-arbitrary-python-expression-from-c)18  - [Как извлечь значения C из объекта Python?](https://python-all.ru/2.7/faq/extending.html#how-do-i-extract-c-values-from-a-python-object)19  - [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/2.7/faq/extending.html#how-do-i-use-py-buildvalue-to-create-a-tuple-of-arbitrary-length)20  - [Как вызвать метод объекта из C?](https://python-all.ru/2.7/faq/extending.html#how-do-i-call-an-object-s-method-from-c)21  - [Как перехватить вывод PyErr\_Print() (или любого другого вывода в stdout/stderr)?](https://python-all.ru/2.7/faq/extending.html#how-do-i-catch-the-output-from-pyerr-print-or-anything-that-prints-to-stdout-stderr)22  - [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/2.7/faq/extending.html#how-do-i-access-a-module-written-in-python-from-c)23  - [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/2.7/faq/extending.html#how-do-i-interface-to-c-objects-from-python)24  - [Почему make завершается ошибкой после добавления модуля через файл Setup?](https://python-all.ru/2.7/faq/extending.html#i-added-a-module-using-the-setup-file-and-the-make-fails-why)25  - [Как отлаживать расширение?](https://python-all.ru/2.7/faq/extending.html#how-do-i-debug-an-extension)26  - [Почему при компиляции модуля Python в Linux отсутствуют некоторые файлы?](https://python-all.ru/2.7/faq/extending.html#i-want-to-compile-a-python-module-on-my-linux-system-but-some-files-are-missing-why)27  - [Что означает «SystemError: \_PyImport\_FixupExtension: module yourmodule not loaded»?](https://python-all.ru/2.7/faq/extending.html#what-does-systemerror-pyimport-fixupextension-module-yourmodule-not-loaded-mean)28  - [Как отличить «неполный ввод» от «неверного ввода»?](https://python-all.ru/2.7/faq/extending.html#how-do-i-tell-incomplete-input-from-invalid-input)29  - [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/2.7/faq/extending.html#how-do-i-find-undefined-g-symbols-builtin-new-or-pure-virtual)30  - [Можно ли создать класс объекта, в котором часть методов реализована на C, а часть – на Python (например, через наследование)?](https://python-all.ru/2.7/faq/extending.html#can-i-create-an-object-class-with-some-methods-implemented-in-c-and-others-in-python-e-g-through-inheritance)31  - [Почему при импорте модуля X возникает ошибка «undefined symbol: PyUnicodeUCS2\*»?](https://python-all.ru/2.7/faq/extending.html#when-importing-module-x-why-do-i-get-undefined-symbol-pyunicodeucs2)3233## [Могу ли я создавать собственные функции на C?](https://python-all.ru/2.7/faq/extending.html#id3)3435Да, можно создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы на C. Это описано в документе [Extending and Embedding the Python Interpreter](https://python-all.ru/2.7/extending/index.html#extending-index).3637Эта тема также освещается в большинстве книг по Python для среднего и продвинутого уровня.3839## [Могу ли я создавать собственные функции на C++?](https://python-all.ru/2.7/faq/extending.html#id4)4041Да, это возможно благодаря средствам совместимости с C в C++. Следует поместить `extern "C" { ... }` вокруг включаемых файлов Python и добавить `extern "C"` перед каждой функцией, которая будет вызываться интерпретатором Python. Глобальные или статические объекты C++ с конструкторами, вероятно, лучше не использовать.4243## [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/2.7/faq/extending.html#id5)4445Существует ряд альтернатив написанию собственных расширений на C – в зависимости от решаемой задачи.4647Если требуется более высокая скорость, [Psyco](https://python-all.ru/2.7/faq/extending.html) генерирует x86\\nассемблерный код из байт-кода Python. Psyco позволяет скомпилировать наиболее критичные по времени функции в коде и добиться значительного прироста производительности с очень небольшими усилиями, при условии работы на машине с x86-совместимым процессором.4849[Cython](https://python-all.ru/2.7/faq/extending.html) и его родственный [Pyrex](https://python-all.ru/2.7/faq/extending.html) – это компиляторы, которые принимают слегка изменённую форму Python и генерируют соответствующий код на C. Pyrex позволяет писать расширения без необходимости изучать C API Python.5051Если требуется взаимодействие с библиотекой на C или C++, для которой в настоящее время нет расширения Python, можно попробовать обернуть типы данных и функции библиотеки с помощью такого инструмента, как [SWIG](https://python-all.ru/2.7/faq/extending.html). [SIP](https://python-all.ru/2.7/faq/extending.html), [CXX](https://python-all.ru/2.7/faq/extending.html), [Boost](https://python-all.ru/2.7/faq/extending.html) или [Weave](https://python-all.ru/2.7/faq/extending.html) также являются альтернативами для обёртывания библиотек C++.5253## [Как выполнить произвольные операторы Python из C?](https://python-all.ru/2.7/faq/extending.html#id6)5455Самая высокоуровневая функция для этого – [`PyRun_SimpleString()`](https://python-all.ru/2.7/c-api/veryhigh.html#c.PyRun_SimpleString), которая принимает единственный строковый аргумент для выполнения в контексте модуля `__main__` и возвращает 0 при успехе и -1, если возникло исключение (включая `SyntaxError`). Если нужен более тонкий контроль, используйте [`PyRun_String()`](https://python-all.ru/2.7/c-api/veryhigh.html#c.PyRun_String); см. исходный код [`PyRun_SimpleString()`](https://python-all.ru/2.7/c-api/veryhigh.html#c.PyRun_SimpleString) в `Python/pythonrun.c`.5657## [Как вычислить произвольное выражение Python из C?](https://python-all.ru/2.7/faq/extending.html#id7)5859Вызовите функцию [`PyRun_String()`](https://python-all.ru/2.7/c-api/veryhigh.html#c.PyRun_String) из предыдущего вопроса с начальным символом [`Py_eval_input`](https://python-all.ru/2.7/c-api/veryhigh.html#c.Py_eval_input); она разбирает выражение, вычисляет его и возвращает его значение.6061## [Как извлечь значения C из объекта Python?](https://python-all.ru/2.7/faq/extending.html#id8)6263Это зависит от типа объекта. Если это кортеж, [`PyTuple_Size()`](https://python-all.ru/2.7/c-api/tuple.html#c.PyTuple_Size) возвращает его длину, а [`PyTuple_GetItem()`](https://python-all.ru/2.7/c-api/tuple.html#c.PyTuple_GetItem) возвращает элемент по указанному индексу. У списков есть аналогичные функции: `PyListSize()` и [`PyList_GetItem()`](https://python-all.ru/2.7/c-api/list.html#c.PyList_GetItem).6465Для строк [`PyString_Size()`](https://python-all.ru/2.7/c-api/string.html#c.PyString_Size) возвращает их длину, а [`PyString_AsString()`](https://python-all.ru/2.7/c-api/string.html#c.PyString_AsString) – указатель на значение. Учтите, что строки Python могут содержать нулевые байты, поэтому C'шный `strlen()` не следует использовать.6667Чтобы проверить тип объекта, сначала убедитесь, что он не равен *NULL*, а затем используйте [`PyString_Check()`](https://python-all.ru/2.7/c-api/string.html#c.PyString_Check), [`PyTuple_Check()`](https://python-all.ru/2.7/c-api/tuple.html#c.PyTuple_Check), [`PyList_Check()`](https://python-all.ru/2.7/c-api/list.html#c.PyList_Check) и т.д.6869Существует также высокоуровневый API для объектов Python, предоставляемый так называемым «абстрактным» интерфейсом – подробнее см. `Include/abstract.h`. Он позволяет взаимодействовать с любыми последовательностями Python с помощью вызовов вроде [`PySequence_Length()`](https://python-all.ru/2.7/c-api/sequence.html#c.PySequence_Length), [`PySequence_GetItem()`](https://python-all.ru/2.7/c-api/sequence.html#c.PySequence_GetItem) и т.д.), а также множества других полезных протоколов.7071## [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/2.7/faq/extending.html#id9)7273Нельзя. Вместо этого используйте `t = PyTuple_New(n)` и заполняйте его объектами с помощью `PyTuple_SetItem(t, i, o)` – учтите, что этот вызов «съедает» одно вхождение счётчика ссылок `o`, поэтому нужно [`Py_INCREF()`](https://python-all.ru/2.7/c-api/refcounting.html#c.Py_INCREF) его. У списков есть аналогичные функции `PyList_New(n)` и `PyList_SetItem(l, i, o)`. Обратите внимание, что *необходимо* установить все элементы кортежа в какие-либо значения перед передачей кортежа коду Python – `PyTuple_New(n)` инициализирует их в NULL, а это не является допустимым значением Python.7475## [Как вызвать метод объекта из C?](https://python-all.ru/2.7/faq/extending.html#id10)7677Функцию [`PyObject_CallMethod()`](https://python-all.ru/2.7/c-api/object.html#c.PyObject_CallMethod) можно использовать для вызова произвольного метода объекта. Параметры: объект, имя вызываемого метода, строка форматирования, подобная используемой с [`Py_BuildValue()`](https://python-all.ru/2.7/c-api/arg.html#c.Py_BuildValue), и значения аргументов:7879```c80PyObject *81PyObject_CallMethod(PyObject *object, char *method_name,82                    char *arg_format, ...);83```8485Это работает для любого объекта, имеющего методы – как встроенные, так и определённые пользователем. Вы отвечаете за последующий [`Py_DECREF()`](https://python-all.ru/2.7/c-api/refcounting.html#c.Py_DECREF) возвращаемого значения.8687Например, чтобы вызвать метод “seek” файлового объекта с аргументами 10, 0 (предполагая, что указатель на файловый объект – “f”):8889```c90res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);91if (res == NULL) {92        ... an exception occurred ...93}94else {95        Py_DECREF(res);96}97```9899Обратите внимание, что [`PyObject_CallObject()`](https://python-all.ru/2.7/c-api/object.html#c.PyObject_CallObject) *всегда* ожидает кортеж в качестве списка аргументов; чтобы вызвать функцию без аргументов, передайте “()” в качестве формата, а чтобы вызвать функцию с одним аргументом, заключите аргумент в круглые скобки, например “(i)”.100101## [Как перехватить вывод PyErr\_Print() (или всего, что печатает в stdout/stderr)?](https://python-all.ru/2.7/faq/extending.html#id11)102103В коде Python определите объект, поддерживающий метод `write()`. Назначьте этот объект на [`sys.stdout`](https://python-all.ru/2.7/library/sys.html#sys.stdout) и [`sys.stderr`](https://python-all.ru/2.7/library/sys.html#sys.stderr). Вызовите print\_error или просто позвольте стандартному механизму трассировки сработать. Тогда вывод будет направлен туда, куда его отправляет метод `write()`.104105Самый простой способ сделать это – использовать класс StringIO из стандартной библиотеки.106107Пример кода и использование для перехвата stdout:108109```pycon110>>> class StdoutCatcher:111...     def __init__(self):112...         self.data = ''113...     def write(self, stuff):114...         self.data = self.data + stuff115...116>>> import sys117>>> sys.stdout = StdoutCatcher()118>>> print 'foo'119>>> print 'hello world!'120>>> sys.stderr.write(sys.stdout.data)121foo122hello world!123```124125## [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/2.7/faq/extending.html#id12)126127Указатель на объект модуля можно получить следующим образом:128129```c130module = PyImport_ImportModule("<modulename>");131```132133Если модуль ещё не был импортирован (т.е. его ещё нет в [`sys.modules`](https://python-all.ru/2.7/library/sys.html#sys.modules)), то выполняется его инициализация; в противном случае просто возвращается значение `sys.modules["<modulename>"]`. Обратите внимание: модуль не добавляется ни в одно пространство имён – гарантируется только его инициализация и сохранение в [`sys.modules`](https://python-all.ru/2.7/library/sys.html#sys.modules).134135Затем можно обратиться к атрибутам модуля (т.е. к любым именам, определённым в модуле) следующим образом:136137```c138attr = PyObject_GetAttrString(module, "<attrname>");139```140141Вызов [`PyObject_SetAttrString()`](https://python-all.ru/2.7/c-api/object.html#c.PyObject_SetAttrString) для присваивания переменным в модуле также работает.142143## [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/2.7/faq/extending.html#id13)144145В зависимости от требований существует множество подходов. Для ручного выполнения начните с чтения [документа «Extending and Embedding»](https://python-all.ru/2.7/extending/index.html#extending-index). Следует понимать, что для системы времени выполнения Python нет большой разницы между C и C++ – поэтому стратегия создания нового типа Python на основе C-структуры (указателя) будет работать и для объектов C++.146147Для библиотек C++ см. [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/2.7/faq/extending.html#c-wrapper-software).148149## [Я добавил модуль через файл Setup, но make завершается ошибкой; почему?](https://python-all.ru/2.7/faq/extending.html#id14)150151Файл Setup должен заканчиваться символом новой строки; если его нет, сборка завершается ошибкой. (Исправление требует уродливых ухищрений в shell-скриптах, и этот баг настолько незначителен, что не стоит затраченных усилий.)152153## [Как отлаживать расширение?](https://python-all.ru/2.7/faq/extending.html#id15)154155При использовании GDB с динамически загружаемыми расширениями невозможно установить точку останова в расширении до его загрузки.156157В файле `.gdbinit` (или интерактивно) добавьте команду:158159```text160br _PyImport_LoadDynamicModule161```162163Затем при запуске GDB:164165```console166$ gdb /local/bin/python167gdb) run myscript.py168gdb) continue # repeat until your extension is loaded169gdb) finish   # so that your extension is loaded170gdb) br myfunction.c:50171gdb) continue172```173174## [Хочу скомпилировать модуль Python в своей системе Linux, но не хватает некоторых файлов. Почему?](https://python-all.ru/2.7/faq/extending.html#id16)175176Большинство собранных версий Python не включают каталог `/usr/lib/python2.x/config/`, который содержит различные файлы необходимые для компиляции расширений Python.177178Для Red Hat установите пакет python-devel RPM, чтобы получить необходимые файлы.179180Для Debian выполните `apt-get install python-dev`.181182## [Что означает сообщение «SystemError: \_PyImport\_FixupExtension: module yourmodule not loaded»?](https://python-all.ru/2.7/faq/extending.html#id17)183184Это означает, что вы создали модуль расширения с именем «yourmodule», но ваша функция инициализации модуля не выполняет инициализацию с этим именем.185186Каждая функция инициализации модуля будет содержать строку, подобную этой:187188```c189module = Py_InitModule("yourmodule", yourmodule_functions);190```191192Если строка, переданная этой функции, не совпадает с именем вашего модуля расширения, будет возбуждено исключение [`SystemError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.SystemError).193194## [Как отличить «неполный ввод» от «недопустимого ввода»?](https://python-all.ru/2.7/faq/extending.html#id18)195196Иногда требуется эмулировать поведение интерактивного интерпретатора Python, где выдаётся приглашение продолжения ввода, когда ввод неполон (например, напечатано начало инструкции «if» или не закрыты скобки или тройные кавычки), но при недопустимом вводе сразу выдаётся сообщение об ошибке синтаксиса.197198В Python можно использовать модуль [`codeop`](https://python-all.ru/2.7/library/codeop.html#module-codeop), который достаточно точно воспроизводит поведение парсера. Например, так делает IDLE.199200Самый простой способ сделать это в C – вызвать [`PyRun_InteractiveLoop()`](https://python-all.ru/2.7/c-api/veryhigh.html#c.PyRun_InteractiveLoop) (возможно, в отдельном потоке) и позволить интерпретатору Python обработать ввод за вас. Также можно установить `PyOS_ReadlineFunctionPointer()` так, чтобы он указывал на вашу пользовательскую функцию ввода. Дополнительные подсказки см. в `Modules/readline.c` и `Parser/myreadline.c`.201202Однако иногда приходится запускать встроенный интерпретатор Python в том же потоке, что и остальное приложение, и нельзя позволять [`PyRun_InteractiveLoop()`](https://python-all.ru/2.7/c-api/veryhigh.html#c.PyRun_InteractiveLoop) останавливаться в ожидании ввода пользователя. Одно из решений – вызвать `PyParser_ParseString()` и проверить, равно ли `e.error` `E_EOF`, что означает, что ввод неполный. Вот пример фрагмента кода, не проверенный, вдохновлённый кодом Алекса Фарбера:203204```c205#include <Python.h>206#include <node.h>207#include <errcode.h>208#include <grammar.h>209#include <parsetok.h>210#include <compile.h>211212int testcomplete(char *code)213  /* код должен заканчиваться на \n */214  /* возвращает -1 при ошибке, 0 при неполном коде, 1 при завершённом */215{216  node *n;217  perrdetail e;218219  n = PyParser_ParseString(code, &_PyParser_Grammar,220                           Py_file_input, &e);221  if (n == NULL) {222    if (e.error == E_EOF)223      return 0;224    return -1;225  }226227  PyNode_Free(n);228  return 1;229}230```231232Другое решение – попробовать скомпилировать полученную строку с помощью [`Py_CompileString()`](https://python-all.ru/2.7/c-api/veryhigh.html#c.Py_CompileString). Если она компилируется без ошибок, выполнить возвращённый объект кода вызовом [`PyEval_EvalCode()`](https://python-all.ru/2.7/c-api/veryhigh.html#c.PyEval_EvalCode). В противном случае сохранить ввод для последующей обработки. Если компиляция не удалась, выяснить, является ли это ошибкой или требуется ещё ввод, извлекая строку сообщения из кортежа исключения и сравнивая её со строкой «unexpected EOF while parsing». Вот полный пример с использованием библиотеки GNU readline (возможно, стоит игнорировать **SIGINT** при вызове readline()):233234```c235#include <stdio.h>236#include <readline.h>237238#include <Python.h>239#include <object.h>240#include <compile.h>241#include <eval.h>242243int main (int argc, char* argv[])244{245  int i, j, done = 0;                          /* длины строки и кода */246  char ps1[] = ">>> ";247  char ps2[] = "... ";248  char *prompt = ps1;249  char *msg, *line, *code = NULL;250  PyObject *src, *glb, *loc;251  PyObject *exc, *val, *trb, *obj, *dum;252253  Py_Initialize ();254  loc = PyDict_New ();255  glb = PyDict_New ();256  PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());257258  while (!done)259  {260    line = readline (prompt);261262    if (NULL == line)                          /* нажата Ctrl-D */263    {264      done = 1;265    }266    else267    {268      i = strlen (line);269270      if (i > 0)271        add_history (line);                    /* сохранять непустые строки */272273      if (NULL == code)                        /* в коде пока ничего нет */274        j = 0;275      else276        j = strlen (code);277278      code = realloc (code, i + j + 2);279      if (NULL == code)                        /* недостаточно памяти */280        exit (1);281282      if (0 == j)                              /* код был пуст, поэтому */283        code[0] = '\0';                        /* чтобы strncat работал корректно */284285      strncat (code, line, i);                 /* добавить строку к коду */286      code[i + j] = '\n';                      /* добавить '\n' к коду */287      code[i + j + 1] = '\0';288289      src = Py_CompileString (code, "<stdin>", Py_single_input);290291      if (NULL != src)                         /* скомпилировано успешно - */292      {293        if (ps1  == prompt ||                  /* ">>> " или */294            '\n' == code[i + j - 1])           /* "... " и двойной '\n' */295        {                                               /* поэтому выполнить его */296          dum = PyEval_EvalCode ((PyCodeObject *)src, glb, loc);297          Py_XDECREF (dum);298          Py_XDECREF (src);299          free (code);300          code = NULL;301          if (PyErr_Occurred ())302            PyErr_Print ();303          prompt = ps1;304        }305      }                                        /* синтаксическая ошибка или E_EOF? */306      else if (PyErr_ExceptionMatches (PyExc_SyntaxError))307      {308        PyErr_Fetch (&exc, &val, &trb);        /* сбрасывает исключение! */309310        if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&311            !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */312        {313          Py_XDECREF (exc);314          Py_XDECREF (val);315          Py_XDECREF (trb);316          prompt = ps2;317        }318        else                                   /* какая-то другая синтаксическая ошибка */319        {320          PyErr_Restore (exc, val, trb);321          PyErr_Print ();322          free (code);323          code = NULL;324          prompt = ps1;325        }326      }327      else                                     /* какая-то не-синтаксическая ошибка */328      {329        PyErr_Print ();330        free (code);331        code = NULL;332        prompt = ps1;333      }334335      free (line);336    }337  }338339  Py_XDECREF(glb);340  Py_XDECREF(loc);341  Py_Finalize();342  exit(0);343}344```345346## [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/2.7/faq/extending.html#id19)347348Для динамической загрузки модулей расширения g++ необходимо перекомпилировать Python, перекомпоновать его с помощью g++ (измените LINKCC в Makefile модулей Python) и скомпоновать модуль расширения с помощью g++ (например, `g++ -shared -o mymodule.so mymodule.o`).349350## [Можно ли создать класс объектов, в котором одни методы реализованы на C, а другие – на Python (например, через наследование)?](https://python-all.ru/2.7/faq/extending.html#id20)351352Да, можно наследовать от встроенных классов, таких как [`int`](https://python-all.ru/2.7/library/functions.html#int), `list`, [`dict`](https://python-all.ru/2.7/library/stdtypes.html#dict) и т.д.353354Библиотека Boost.Python (BPL, [http://www.boost.org/libs/python/doc/index.html](https://python-all.ru/2.7/faq/extending.html)) предоставляет способ сделать это из C++ (т.е. вы можете наследоваться от класса расширения, написанного на C++, с помощью BPL).355356## [Почему при импорте модуля X возникает ошибка «undefined symbol: PyUnicodeUCS2\*»?](https://python-all.ru/2.7/faq/extending.html#id21)357358Используется версия Python с 4-байтовым представлением символов Unicode, но некоторый модуль расширения на C, который импортируется, был скомпилирован с помощью Python, использующего 2-байтовое представление (по умолчанию).359360Если же имя неопределённого символа начинается с `PyUnicodeUCS4`, проблема обратная: Python был собран с использованием двухбайтовых символов Unicode, а модуль расширения скомпилирован с Python, использующим четырёхбайтовые символы Unicode.361362Это легко может произойти при использовании предварительно собранных пакетов расширений. В частности, RedHat Linux 7.x поставлял бинарный файл «python2», скомпилированный с четырёхбайтовым Unicode. Это вызывает сбой при связывании, только если расширение использует любую из функций `PyUnicode_*()`. Проблема также возникает, если расширение использует любые форматные спецификаторы, связанные с Unicode, для [`Py_BuildValue()`](https://python-all.ru/2.7/c-api/arg.html#c.Py_BuildValue) (или аналогичных) или спецификации параметров для [`PyArg_ParseTuple()`](https://python-all.ru/2.7/c-api/arg.html#c.PyArg_ParseTuple).363364Размер символа Unicode, используемого интерпретатором Python, можно определить по значению sys.maxunicode:365366```pycon367>>> import sys368>>> if sys.maxunicode > 65535:369...     print 'UCS4 build'370... else:371...     print 'UCS2 build'372```373374Единственный способ решить эту проблему – использовать модули расширения, скомпилированные с двоичным файлом Python, собранным с тем же размером символов Unicode.375