> **Источник:** https://python-all.ru/3.1/faq/extending.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# [FAQ по расширению и встраиванию](https://python-all.ru/3.1/faq/extending.html#id6)

Содержание

- [FAQ по расширению и встраиванию](https://python-all.ru/3.1/faq/extending.html#extending-embedding-faq)

  - [Можно ли создавать собственные функции на C?](https://python-all.ru/3.1/faq/extending.html#can-i-create-my-own-functions-in-c)
  - [Можно ли создавать собственные функции на C++?](https://python-all.ru/3.1/faq/extending.html#id1)
  - [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.1/faq/extending.html#writing-c-is-hard-are-there-any-alternatives)
  - [Как выполнить произвольные инструкции Python из C?](https://python-all.ru/3.1/faq/extending.html#how-can-i-execute-arbitrary-python-statements-from-c)
  - [Как вычислить произвольное выражение Python из C?](https://python-all.ru/3.1/faq/extending.html#how-can-i-evaluate-an-arbitrary-python-expression-from-c)
  - [Как извлечь значения C из объекта Python?](https://python-all.ru/3.1/faq/extending.html#how-do-i-extract-c-values-from-a-python-object)
  - [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/3.1/faq/extending.html#how-do-i-use-py-buildvalue-to-create-a-tuple-of-arbitrary-length)
  - [Как вызвать метод объекта из C?](https://python-all.ru/3.1/faq/extending.html#how-do-i-call-an-object-s-method-from-c)
  - [Как перехватить вывод PyErr\_Print() (или любого другого вывода в stdout/stderr)?](https://python-all.ru/3.1/faq/extending.html#how-do-i-catch-the-output-from-pyerr-print-or-anything-that-prints-to-stdout-stderr)
  - [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/3.1/faq/extending.html#how-do-i-access-a-module-written-in-python-from-c)
  - [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/3.1/faq/extending.html#how-do-i-interface-to-c-objects-from-python)
  - [Почему make завершается ошибкой после добавления модуля через файл Setup?](https://python-all.ru/3.1/faq/extending.html#i-added-a-module-using-the-setup-file-and-the-make-fails-why)
  - [Как отлаживать расширение?](https://python-all.ru/3.1/faq/extending.html#how-do-i-debug-an-extension)
  - [Почему при компиляции модуля Python в Linux отсутствуют некоторые файлы?](https://python-all.ru/3.1/faq/extending.html#i-want-to-compile-a-python-module-on-my-linux-system-but-some-files-are-missing-why)
  - [Что означает «SystemError: \_PyImport\_FixupExtension: module yourmodule not loaded»?](https://python-all.ru/3.1/faq/extending.html#what-does-systemerror-pyimport-fixupextension-module-yourmodule-not-loaded-mean)
  - [Как отличить «неполный ввод» от «неверного ввода»?](https://python-all.ru/3.1/faq/extending.html#how-do-i-tell-incomplete-input-from-invalid-input)
  - [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/3.1/faq/extending.html#how-do-i-find-undefined-g-symbols-builtin-new-or-pure-virtual)
  - [Можно ли создать класс объекта, в котором часть методов реализована на C, а часть – на Python (например, через наследование)?](https://python-all.ru/3.1/faq/extending.html#can-i-create-an-object-class-with-some-methods-implemented-in-c-and-others-in-python-e-g-through-inheritance)
  - [Почему при импорте модуля X возникает ошибка «undefined symbol: PyUnicodeUCS2\*»?](https://python-all.ru/3.1/faq/extending.html#when-importing-module-x-why-do-i-get-undefined-symbol-pyunicodeucs2)

## [Могу ли я создавать собственные функции на C?](https://python-all.ru/3.1/faq/extending.html#id7)

Да, можно создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы на C. Это описано в документе [*Extending and Embedding the Python Interpreter*](https://python-all.ru/3.1/extending/index.html#extending-index).

Эта тема также освещается в большинстве книг по Python для среднего и продвинутого уровня.

## [Могу ли я создавать собственные функции на C++?](https://python-all.ru/3.1/faq/extending.html#id8)

Да, используя возможности совместимости с C, имеющиеся в C++. Разместите `extern "C" { ... }` вокруг включаемых файлов Python и добавьте `extern "C"` перед каждой функцией, которая будет вызываться интерпретатором Python. Глобальные или статические объекты C++ с конструкторами, вероятно, не лучшая идея.

## [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.1/faq/extending.html#id9)

Существует ряд альтернатив написанию собственных расширений на C – в зависимости от решаемой задачи.

Если требуется более высокая скорость, [Psyco](https://python-all.ru/3.1/faq/extending.html) генерирует x86\\nассемблерный код из байт-кода Python. Psyco позволяет скомпилировать наиболее критичные по времени функции в коде и добиться значительного прироста производительности с очень небольшими усилиями, при условии работы на машине с x86-совместимым процессором.

[Pyrex](https://python-all.ru/3.1/faq/extending.html) – это компилятор,\\nкоторый принимает слегка изменённую форму Python и генерирует соответствующий C-код. Pyrex позволяет писать расширения без необходимости изучать C API Python.

Если необходимо взаимодействовать с какой-либо библиотекой на C или C++, для которой пока не существует расширения Python,\\nможно попробовать обернуть типы данных и функции библиотеки с помощью такого инструмента, как [SWIG](https://python-all.ru/3.1/faq/extending.html). [SIP](https://python-all.ru/3.1/faq/extending.html), [CXX](https://python-all.ru/3.1/faq/extending.html), [Boost](https://python-all.ru/3.1/faq/extending.html) или [Weave](https://python-all.ru/3.1/faq/extending.html) также являются альтернативами для обёртывания библиотек C++.

## [Как выполнить произвольные операторы Python из C?](https://python-all.ru/3.1/faq/extending.html#id10)

Самая высокоуровневая функция для этого – [`PyRun_SimpleString()`](https://python-all.ru/3.1/c-api/veryhigh.html#PyRun_SimpleString), которая принимает один строковый аргумент, выполняемый в контексте модуля `__main__` и возвращает 0 при успехе и -1, если возникло исключение (включая `SyntaxError`). Если нужно больше контроля, используйте [`PyRun_String()`](https://python-all.ru/3.1/c-api/veryhigh.html#PyRun_String); см. исходный код [`PyRun_SimpleString()`](https://python-all.ru/3.1/c-api/veryhigh.html#PyRun_SimpleString) в `Python/pythonrun.c`.

## [Как вычислить произвольное выражение Python из C?](https://python-all.ru/3.1/faq/extending.html#id11)

Вызовите функцию [`PyRun_String()`](https://python-all.ru/3.1/c-api/veryhigh.html#PyRun_String) из предыдущего вопроса с начальным символом [`Py_eval_input`](https://python-all.ru/3.1/c-api/veryhigh.html#Py_eval_input); она разбирает выражение, вычисляет его и возвращает его значение.

## [Как извлечь значения C из объекта Python?](https://python-all.ru/3.1/faq/extending.html#id12)

Это зависит от типа объекта. Если это кортеж, [`PyTuple_Size()`](https://python-all.ru/3.1/c-api/tuple.html#PyTuple_Size) возвращает его длину, а [`PyTuple_GetItem()`](https://python-all.ru/3.1/c-api/tuple.html#PyTuple_GetItem) возвращает элемент по указанному индексу. Списки имеют похожие функции: `PyListSize()` и [`PyList_GetItem()`](https://python-all.ru/3.1/c-api/list.html#PyList_GetItem).

Для строк `PyString_Size()` возвращает их длину, а\\n`PyString_AsString()` – указатель на значение. Обратите внимание, что строки Python могут содержать нулевые байты, поэтому не следует использовать C-функцию `strlen()`.

Чтобы проверить тип объекта, сначала убедитесь, что он не равен *NULL*, а затем используйте\\n`PyString_Check()`, [`PyTuple_Check()`](https://python-all.ru/3.1/c-api/tuple.html#PyTuple_Check), [`PyList_Check()`](https://python-all.ru/3.1/c-api/list.html#PyList_Check) и т.д.

Существует также высокоуровневый API для объектов Python, предоставляемый так называемым «абстрактным» интерфейсом – читайте `Include/abstract.h` для получения дополнительных\\nсведений. Он позволяет взаимодействовать с любыми последовательностями Python с помощью вызовов типа [`PySequence_Length()`](https://python-all.ru/3.1/c-api/sequence.html#PySequence_Length), [`PySequence_GetItem()`](https://python-all.ru/3.1/c-api/sequence.html#PySequence_GetItem) и т.д.), а также многих других полезных протоколов.

## [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/3.1/faq/extending.html#id13)

Нельзя. Вместо этого используйте `t = PyTuple_New(n)` и заполните его объектами с помощью\\n`PyTuple_SetItem(t, i, o)` – обратите внимание, что это «поглощает» счётчик ссылок объекта\\n`o`, поэтому его необходимо увеличить с помощью [`Py_INCREF()`](https://python-all.ru/3.1/c-api/refcounting.html#Py_INCREF). Списки имеют аналогичные функции\\n`PyList_New(n)` и `PyList_SetItem(l, i, o)`. Обратите внимание, что *необходимо* установить все элементы кортежа в некоторые значения перед передачей кортежа в код Python –\\n`PyTuple_New(n)` инициализирует их значением NULL, которое не является допустимым значением Python.

## [Как вызвать метод объекта из C?](https://python-all.ru/3.1/faq/extending.html#id14)

Функция [`PyObject_CallMethod()`](https://python-all.ru/3.1/c-api/object.html#PyObject_CallMethod) может использоваться для вызова произвольного метода объекта. Параметры: объект, имя вызываемого метода, строка формата, подобная используемой в [`Py_BuildValue()`](https://python-all.ru/3.1/c-api/arg.html#Py_BuildValue), и значения аргументов:

```c
PyObject *
PyObject_CallMethod(PyObject *object, char *method_name,
                    char *arg_format, ...);
```

Это работает для любого объекта, имеющего методы – встроенные или определённые пользователем. Необходимо в конечном счёте выполнить [`Py_DECREF()`](https://python-all.ru/3.1/c-api/refcounting.html#Py_DECREF) возвращаемого значения.

Например, чтобы вызвать метод “seek” файлового объекта с аргументами 10, 0 (предполагая, что указатель на файловый объект – “f”):

```c
res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}
```

Обратите внимание: поскольку [`PyObject_CallObject()`](https://python-all.ru/3.1/c-api/object.html#PyObject_CallObject) *всегда* требует кортеж для списка аргументов, для вызова функции без аргументов передайте «()» в качестве формата, а для вызова функции с одним аргументом заключите аргумент в круглые скобки, например, «(i)».

## [Как перехватить вывод PyErr\_Print() (или всего, что печатает в stdout/stderr)?](https://python-all.ru/3.1/faq/extending.html#id15)

В коде Python определите объект, поддерживающий метод `write()`. Назначьте этот объект [`sys.stdout`](https://python-all.ru/3.1/library/sys.html#sys.stdout) и [`sys.stderr`](https://python-all.ru/3.1/library/sys.html#sys.stderr). Вызовите print\_error или просто позвольте сработать стандартному механизму трассировки. Тогда вывод будет направлен туда, куда его отправляет ваш метод `write()`.

Самый простой способ сделать это – использовать класс StringIO из стандартной библиотеки.

Пример кода и использование для перехвата stdout:

```c
>>> class StdoutCatcher:
...     def __init__(self):
...         self.data = ''
...     def write(self, stuff):
...         self.data = self.data + stuff
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.data)
foo
hello world!
```

## [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/3.1/faq/extending.html#id16)

Указатель на объект модуля можно получить следующим образом:

```c
module = PyImport_ImportModule("<modulename>");
```

Если модуль ещё не был импортирован (то есть отсутствует в [`sys.modules`](https://python-all.ru/3.1/library/sys.html#sys.modules)), эта функция инициализирует модуль; в противном случае она просто возвращает значение `sys.modules["<имя_модуля>"]`. Обратите внимание: она не добавляет модуль ни в одно пространство имён – она только гарантирует, что модуль инициализирован и сохранён в [`sys.modules`](https://python-all.ru/3.1/library/sys.html#sys.modules).

Затем можно обратиться к атрибутам модуля (т.е. к любым именам, определённым в модуле) следующим образом:

```c
attr = PyObject_GetAttrString(module, "<attrname>");
```

Вызов [`PyObject_SetAttrString()`](https://python-all.ru/3.1/c-api/object.html#PyObject_SetAttrString) для присваивания переменным модуля также работает.

## [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/3.1/faq/extending.html#id17)

В зависимости от требований существует множество подходов. Для ручного выполнения начните с чтения [*документа «Extending and Embedding»*](https://python-all.ru/3.1/extending/index.html#extending-index). Следует понимать, что для системы времени выполнения Python нет большой разницы между C и C++ – поэтому стратегия создания нового типа Python на основе C-структуры (указателя) будет работать и для объектов C++.

Для библиотек C++ можно обратить внимание на [SIP](https://python-all.ru/3.1/faq/extending.html), [CXX](https://python-all.ru/3.1/faq/extending.html), [Boost](https://python-all.ru/3.1/faq/extending.html), [Weave](https://python-all.ru/3.1/faq/extending.html) или [SWIG](https://python-all.ru/3.1/faq/extending.html)

## [Я добавил модуль через файл Setup, но make завершается ошибкой; почему?](https://python-all.ru/3.1/faq/extending.html#id18)

Файл Setup должен заканчиваться символом новой строки; если его нет, сборка завершается ошибкой. (Исправление требует уродливых ухищрений в shell-скриптах, и этот баг настолько незначителен, что не стоит затраченных усилий.)

## [Как отлаживать расширение?](https://python-all.ru/3.1/faq/extending.html#id19)

При использовании GDB с динамически загружаемыми расширениями невозможно установить точку останова в расширении до его загрузки.

В файл `.gdbinit` (или в интерактивном режиме) добавьте команду:

```c
br _PyImport_LoadDynamicModule
```

Затем при запуске GDB:

```c
$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repeat until your extension is loaded
gdb) finish   # so that your extension is loaded
gdb) br myfunction.c:50
gdb) continue
```

## [Хочу скомпилировать модуль Python в своей системе Linux, но не хватает некоторых файлов. Почему?](https://python-all.ru/3.1/faq/extending.html#id20)

Большинство пакетных версий Python не включают каталог `/usr/lib/python2.x/config/`, который содержит различные файлы, необходимые для компиляции расширений Python.

Для Red Hat установите пакет python-devel RPM, чтобы получить необходимые файлы.

Для Debian выполните `apt-get install python-dev`.

## [Что означает сообщение «SystemError: \_PyImport\_FixupExtension: module yourmodule not loaded»?](https://python-all.ru/3.1/faq/extending.html#id21)

Это означает, что вы создали модуль расширения с именем «yourmodule», но ваша функция инициализации модуля не выполняет инициализацию с этим именем.

Каждая функция инициализации модуля будет содержать строку, подобную этой:

```c
module = Py_InitModule("yourmodule", yourmodule_functions);
```

Если строка, переданная этой функции, не совпадает с именем вашего модуля расширения, будет возбуждено исключение [`SystemError`](https://python-all.ru/3.1/library/exceptions.html#SystemError).

## [Как отличить «неполный ввод» от «недопустимого ввода»?](https://python-all.ru/3.1/faq/extending.html#id22)

Иногда требуется эмулировать поведение интерактивного интерпретатора Python, где выдаётся приглашение продолжения ввода, когда ввод неполон (например, напечатано начало инструкции «if» или не закрыты скобки или тройные кавычки), но при недопустимом вводе сразу выдаётся сообщение об ошибке синтаксиса.

В Python можно использовать модуль [`codeop`](https://python-all.ru/3.1/library/codeop.html#module-codeop), который достаточно точно приближает поведение парсера. Например, его использует IDLE.

Самый простой способ сделать это на C – вызвать [`PyRun_InteractiveLoop()`](https://python-all.ru/3.1/c-api/veryhigh.html#PyRun_InteractiveLoop) (возможно, в отдельном потоке) и позволить интерпретатору Python обрабатывать ввод за вас. Также можно установить `PyOS_ReadlineFunctionPointer()` на свою пользовательскую функцию ввода. См. `Modules/readline.c` и `Parser/myreadline.c` для дополнительных советов.

Однако иногда необходимо запускать встроенный интерпретатор Python в том же потоке, что и остальное приложение, и нельзя позволить [`PyRun_InteractiveLoop()`](https://python-all.ru/3.1/c-api/veryhigh.html#PyRun_InteractiveLoop) останавливаться в ожидании пользовательского ввода. Одно из решений – вызвать `PyParser_ParseString()` и проверить, равно ли `e.error` значению `E_EOF`, что означает, что ввод неполон). Вот пример фрагмента кода, не тестировавшийся, основанный на коде Алекса Фарбера:

```c
#include <Python.h>
#include <node.h>
#include <errcode.h>
#include <grammar.h>
#include <parsetok.h>
#include <compile.h>

int testcomplete(char *code)
  /* код должен заканчиваться на \n */
  /* возвращает -1 при ошибке, 0 при неполном коде, 1 при завершённом */
{
  node *n;
  perrdetail e;

  n = PyParser_ParseString(code, &_PyParser_Grammar,
                           Py_file_input, &e);
  if (n == NULL) {
    if (e.error == E_EOF)
      return 0;
    return -1;
  }

  PyNode_Free(n);
  return 1;
}
```

Другое решение – попробовать скомпилировать полученную строку с помощью [`Py_CompileString()`](https://python-all.ru/3.1/c-api/veryhigh.html#Py_CompileString). Если она компилируется без ошибок, попробуйте выполнить возвращённый объект кода, вызвав [`PyEval_EvalCode()`](https://python-all.ru/3.1/c-api/veryhigh.html#PyEval_EvalCode). В противном случае сохраните ввод для последующей обработки. Если компиляция завершается неудачей, определите, является ли это ошибкой или просто требуется дополнительный ввод – извлеките строку сообщения из кортежа исключения и сравните её со строкой «unexpected EOF while parsing». Вот полный пример с использованием библиотеки GNU readline (возможно, стоит игнорировать **SIGINT** при вызове readline()):

```c
#include <stdio.h>
#include <readline.h>

#include <Python.h>
#include <object.h>
#include <compile.h>
#include <eval.h>

int main (int argc, char* argv[])
{
  int i, j, done = 0;                          /* длины строки и кода */
  char ps1[] = ">>> ";
  char ps2[] = "... ";
  char *prompt = ps1;
  char *msg, *line, *code = NULL;
  PyObject *src, *glb, *loc;
  PyObject *exc, *val, *trb, *obj, *dum;

  Py_Initialize ();
  loc = PyDict_New ();
  glb = PyDict_New ();
  PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());

  while (!done)
  {
    line = readline (prompt);

    if (NULL == line)                          /* нажата CTRL-D */
    {
      done = 1;
    }
    else
    {
      i = strlen (line);

      if (i > 0)
        add_history (line);                    /* сохранять непустые строки */

      if (NULL == code)                        /* в коде пока ничего нет */
        j = 0;
      else
        j = strlen (code);

      code = realloc (code, i + j + 2);
      if (NULL == code)                        /* недостаточно памяти */
        exit (1);

      if (0 == j)                              /* код был пуст, поэтому */
        code[0] = '\0';                        /* чтобы strncat работал корректно */

      strncat (code, line, i);                 /* добавить строку к коду */
      code[i + j] = '\n';                      /* добавить '\n' к коду */
      code[i + j + 1] = '\0';

      src = Py_CompileString (code, "<stdin>", Py_single_input);

      if (NULL != src)                         /* скомпилировано успешно - */
      {
        if (ps1  == prompt ||                  /* ">>> " или */
            '\n' == code[i + j - 1])           /* "... " и двойной '\n' */
        {                                               /* поэтому выполнить его */
          dum = PyEval_EvalCode ((PyCodeObject *)src, glb, loc);
          Py_XDECREF (dum);
          Py_XDECREF (src);
          free (code);
          code = NULL;
          if (PyErr_Occurred ())
            PyErr_Print ();
          prompt = ps1;
        }
      }                                        /* синтаксическая ошибка или E_EOF? */
      else if (PyErr_ExceptionMatches (PyExc_SyntaxError))
      {
        PyErr_Fetch (&exc, &val, &trb);        /* сбрасывает исключение! */

        if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&
            !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */
        {
          Py_XDECREF (exc);
          Py_XDECREF (val);
          Py_XDECREF (trb);
          prompt = ps2;
        }
        else                                   /* какая-то другая синтаксическая ошибка */
        {
          PyErr_Restore (exc, val, trb);
          PyErr_Print ();
          free (code);
          code = NULL;
          prompt = ps1;
        }
      }
      else                                     /* какая-то не-синтаксическая ошибка */
      {
        PyErr_Print ();
        free (code);
        code = NULL;
        prompt = ps1;
      }

      free (line);
    }
  }

  Py_XDECREF(glb);
  Py_XDECREF(loc);
  Py_Finalize();
  exit(0);
}
```

## [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/3.1/faq/extending.html#id23)

Для динамической загрузки модулей расширения g++ необходимо перекомпилировать Python, перелинковать его с помощью g++ (измените LINKCC в Makefile модулей Python) и слинковать ваш модуль расширения с помощью g++ (например, `g++ -shared -o mymodule.so mymodule.o`).

## [Можно ли создать класс объектов, в котором одни методы реализованы на C, а другие – на Python (например, через наследование)?](https://python-all.ru/3.1/faq/extending.html#id24)

В Python 2.2 можно наследовать от встроенных классов, таких как [`int`](https://python-all.ru/3.1/library/functions.html#int),\\n[`list`](https://python-all.ru/3.1/library/functions.html#list), [`dict`](https://python-all.ru/3.1/library/stdtypes.html#dict) и т.д.

Библиотека Boost.Python (BPL, [http://www.boost.org/libs/python/doc/index.html](https://python-all.ru/3.1/faq/extending.html)) предоставляет способ сделать это из C++ (т.е. вы можете наследоваться от класса расширения, написанного на C++, с помощью BPL).

## [Почему при импорте модуля X возникает ошибка «undefined symbol: PyUnicodeUCS2\*»?](https://python-all.ru/3.1/faq/extending.html#id25)

Используется версия Python с 4-байтовым представлением символов Unicode, но некоторый модуль расширения на C, который импортируется, был скомпилирован с помощью Python, использующего 2-байтовое представление (по умолчанию).

Если же имя неопределённого символа начинается с `PyUnicodeUCS4`, то проблема обратная: Python был собран с 2-байтовыми символами Unicode, а модуль расширения – с 4-байтовыми.

Такая ситуация легко возникает при использовании предварительно собранных пакетов расширений. В частности, RedHat Linux 7.x поставлял двоичный файл «python2», скомпилированный с 4-байтовым Unicode. Это приводит к ошибке компоновки только в том случае, если расширение использует любую из функций `PyUnicode_*()`. Проблема также возникает, если расширение использует любые форматные спецификаторы, связанные с Unicode, для [`Py_BuildValue()`](https://python-all.ru/3.1/c-api/arg.html#Py_BuildValue) (или аналогичных) или спецификации параметров для [`PyArg_ParseTuple()`](https://python-all.ru/3.1/c-api/arg.html#PyArg_ParseTuple).

Размер символа Unicode, используемого интерпретатором Python, можно определить по значению sys.maxunicode:

```c
>>> import sys
>>> if sys.maxunicode > 65535:
...     print('UCS4 build')
... else:
...     print('UCS2 build')
```

Единственный способ решить эту проблему – использовать модули расширения, скомпилированные с двоичным файлом Python, собранным с тем же размером символов Unicode.
