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

extending.md

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

1> **Источник:** https://python-all.ru/3.3/faq/extending.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [FAQ по расширению и встраиванию](https://python-all.ru/3.3/faq/extending.html#id2)89Содержание1011- [FAQ по расширению и встраиванию](https://python-all.ru/3.3/faq/extending.html#extending-embedding-faq)1213  - [Можно ли создавать собственные функции на C?](https://python-all.ru/3.3/faq/extending.html#can-i-create-my-own-functions-in-c)14  - [Можно ли создавать собственные функции на C++?](https://python-all.ru/3.3/faq/extending.html#id1)15  - [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.3/faq/extending.html#writing-c-is-hard-are-there-any-alternatives)16  - [Как выполнить произвольные инструкции Python из C?](https://python-all.ru/3.3/faq/extending.html#how-can-i-execute-arbitrary-python-statements-from-c)17  - [Как вычислить произвольное выражение Python из C?](https://python-all.ru/3.3/faq/extending.html#how-can-i-evaluate-an-arbitrary-python-expression-from-c)18  - [Как извлечь значения C из объекта Python?](https://python-all.ru/3.3/faq/extending.html#how-do-i-extract-c-values-from-a-python-object)19  - [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/3.3/faq/extending.html#how-do-i-use-py-buildvalue-to-create-a-tuple-of-arbitrary-length)20  - [Как вызвать метод объекта из C?](https://python-all.ru/3.3/faq/extending.html#how-do-i-call-an-object-s-method-from-c)21  - [Как перехватить вывод PyErr\_Print() (или любого другого вывода в stdout/stderr)?](https://python-all.ru/3.3/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/3.3/faq/extending.html#how-do-i-access-a-module-written-in-python-from-c)23  - [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/3.3/faq/extending.html#how-do-i-interface-to-c-objects-from-python)24  - [Почему make завершается ошибкой после добавления модуля через файл Setup?](https://python-all.ru/3.3/faq/extending.html#i-added-a-module-using-the-setup-file-and-the-make-fails-why)25  - [Как отлаживать расширение?](https://python-all.ru/3.3/faq/extending.html#how-do-i-debug-an-extension)26  - [Почему при компиляции модуля Python в Linux отсутствуют некоторые файлы?](https://python-all.ru/3.3/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/3.3/faq/extending.html#what-does-systemerror-pyimport-fixupextension-module-yourmodule-not-loaded-mean)28  - [Как отличить «неполный ввод» от «неверного ввода»?](https://python-all.ru/3.3/faq/extending.html#how-do-i-tell-incomplete-input-from-invalid-input)29  - [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/3.3/faq/extending.html#how-do-i-find-undefined-g-symbols-builtin-new-or-pure-virtual)30  - [Можно ли создать класс объекта, в котором часть методов реализована на C, а часть – на Python (например, через наследование)?](https://python-all.ru/3.3/faq/extending.html#can-i-create-an-object-class-with-some-methods-implemented-in-c-and-others-in-python-e-g-through-inheritance)3132## [Могу ли я создавать собственные функции на C?](https://python-all.ru/3.3/faq/extending.html#id3)3334Да, можно создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы на C. Это описано в документе [*Extending and Embedding the Python Interpreter*](https://python-all.ru/3.3/extending/index.html#extending-index).3536Эта тема также освещается в большинстве книг по Python для среднего и продвинутого уровня.3738## [Могу ли я создавать собственные функции на C++?](https://python-all.ru/3.3/faq/extending.html#id4)3940Да, используя возможности совместимости с C, имеющиеся в C++. Разместите `extern "C" { ... }` вокруг включаемых файлов Python и добавьте `extern "C"` перед каждой функцией, которая будет вызываться интерпретатором Python. Глобальные или статические объекты C++ с конструкторами, вероятно, не лучшая идея.4142## [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.3/faq/extending.html#id5)4344Существует ряд альтернатив написанию собственных расширений на C – в зависимости от решаемой задачи.4546[Cython](https://python-all.ru/3.3/faq/extending.html) и его родственник [Pyrex](https://python-all.ru/3.3/faq/extending.html) – это компиляторы, которые принимают слегка изменённую форму Python и генерируют соответствующий код на C. Cython и Pyrex позволяют писать расширения, не изучая C API Python.4748Если необходимо взаимодействовать с библиотекой на C или C++, для которой пока нет расширения Python, можно попробовать обернуть типы данных и функции библиотеки с помощью такого инструмента, как [SWIG](https://python-all.ru/3.3/faq/extending.html). [SIP](https://python-all.ru/3.3/faq/extending.html), [CXX](https://python-all.ru/3.3/faq/extending.html), [Boost](https://python-all.ru/3.3/faq/extending.html) или [Weave](https://python-all.ru/3.3/faq/extending.html) – это также альтернативы для обёртывания библиотек C++.4950## [Как выполнить произвольные операторы Python из C?](https://python-all.ru/3.3/faq/extending.html#id6)5152Самая высокоуровневая функция для этого – [`PyRun_SimpleString()`](https://python-all.ru/3.3/c-api/veryhigh.html#PyRun_SimpleString), которая принимает один строковый аргумент, выполняемый в контексте модуля `__main__` и возвращает 0 при успехе и -1, если возникло исключение (включая `SyntaxError`). Если нужно больше контроля, используйте [`PyRun_String()`](https://python-all.ru/3.3/c-api/veryhigh.html#PyRun_String); см. исходный код [`PyRun_SimpleString()`](https://python-all.ru/3.3/c-api/veryhigh.html#PyRun_SimpleString) в `Python/pythonrun.c`.5354## [Как вычислить произвольное выражение Python из C?](https://python-all.ru/3.3/faq/extending.html#id7)5556Вызовите функцию [`PyRun_String()`](https://python-all.ru/3.3/c-api/veryhigh.html#PyRun_String) из предыдущего вопроса с начальным символом [`Py_eval_input`](https://python-all.ru/3.3/c-api/veryhigh.html#Py_eval_input); она разбирает выражение, вычисляет его и возвращает его значение.5758## [Как извлечь значения C из объекта Python?](https://python-all.ru/3.3/faq/extending.html#id8)5960Это зависит от типа объекта. Если это кортеж, [`PyTuple_Size()`](https://python-all.ru/3.3/c-api/tuple.html#PyTuple_Size) возвращает его длину, а [`PyTuple_GetItem()`](https://python-all.ru/3.3/c-api/tuple.html#PyTuple_GetItem) возвращает элемент по указанному индексу. Списки имеют похожие функции: `PyListSize()` и [`PyList_GetItem()`](https://python-all.ru/3.3/c-api/list.html#PyList_GetItem).6162Для bytes: [`PyBytes_Size()`](https://python-all.ru/3.3/c-api/bytes.html#PyBytes_Size) возвращает его длину, а [`PyBytes_AsStringAndSize()`](https://python-all.ru/3.3/c-api/bytes.html#PyBytes_AsStringAndSize) предоставляет указатель на его значение и его длину. Обратите внимание, что объекты bytes в Python могут содержать нулевые байты, поэтому C-функцию `strlen()` использовать не следует.6364Чтобы проверить тип объекта, сначала убедитесь, что он не равен *NULL*, а затем используйте [`PyBytes_Check()`](https://python-all.ru/3.3/c-api/bytes.html#PyBytes_Check), [`PyTuple_Check()`](https://python-all.ru/3.3/c-api/tuple.html#PyTuple_Check), [`PyList_Check()`](https://python-all.ru/3.3/c-api/list.html#PyList_Check) и т.д.6566Существует также высокоуровневый API для объектов Python, предоставляемый так называемым 'абстрактным' интерфейсом – см. `Include/abstract.h` для подробностей. Он позволяет взаимодействовать с любыми последовательностями Python с помощью вызовов, таких как [`PySequence_Length()`](https://python-all.ru/3.3/c-api/sequence.html#PySequence_Length), [`PySequence_GetItem()`](https://python-all.ru/3.3/c-api/sequence.html#PySequence_GetItem) и т.д.), а также многих других полезных протоколов, таких как числа ([`PyNumber_Index()`](https://python-all.ru/3.3/c-api/number.html#PyNumber_Index) и др.) и отображения в API PyMapping.6768## [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/3.3/faq/extending.html#id9)6970Нельзя. Вместо этого используйте [`PyTuple_Pack()`](https://python-all.ru/3.3/c-api/tuple.html#PyTuple_Pack).7172## [Как вызвать метод объекта из C?](https://python-all.ru/3.3/faq/extending.html#id10)7374Функция [`PyObject_CallMethod()`](https://python-all.ru/3.3/c-api/object.html#PyObject_CallMethod) может использоваться для вызова произвольного метода объекта. Параметры: объект, имя вызываемого метода, строка формата, подобная используемой в [`Py_BuildValue()`](https://python-all.ru/3.3/c-api/arg.html#Py_BuildValue), и значения аргументов:7576```c77PyObject *78PyObject_CallMethod(PyObject *object, char *method_name,79                    char *arg_format, ...);80```8182Это работает для любого объекта, имеющего методы – встроенные или определённые пользователем. Необходимо в конечном счёте выполнить [`Py_DECREF()`](https://python-all.ru/3.3/c-api/refcounting.html#Py_DECREF) возвращаемого значения.8384Например, чтобы вызвать метод “seek” файлового объекта с аргументами 10, 0 (предполагая, что указатель на файловый объект – “f”):8586```c87res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);88if (res == NULL) {89        ... an exception occurred ...90}91else {92        Py_DECREF(res);93}94```9596Обратите внимание: поскольку [`PyObject_CallObject()`](https://python-all.ru/3.3/c-api/object.html#PyObject_CallObject) *всегда* требует кортеж для списка аргументов, для вызова функции без аргументов передайте «()» в качестве формата, а для вызова функции с одним аргументом заключите аргумент в круглые скобки, например, «(i)».9798## [Как перехватить вывод PyErr\_Print() (или всего, что печатает в stdout/stderr)?](https://python-all.ru/3.3/faq/extending.html#id11)99100В коде Python определите объект, поддерживающий метод `write()`. Назначьте этот объект [`sys.stdout`](https://python-all.ru/3.3/library/sys.html#sys.stdout) и [`sys.stderr`](https://python-all.ru/3.3/library/sys.html#sys.stderr). Вызовите print\_error или просто позвольте сработать стандартному механизму трассировки. Тогда вывод будет направлен туда, куда его отправляет ваш метод `write()`.101102Самый простой способ сделать это – использовать класс [`io.StringIO`](https://python-all.ru/3.3/library/io.html#io.StringIO):103104```c105>>> import io, sys106>>> sys.stdout = io.StringIO()107>>> print('foo')108>>> print('hello world!')109>>> sys.stderr.write(sys.stdout.getvalue())110foo111hello world!112```113114Пользовательский объект для того же самого мог бы выглядеть так:115116```c117>>> import io, sys118>>> class StdoutCatcher(io.TextIOBase):119...     def __init__(self):120...         self.data = []121...     def write(self, stuff):122...         self.data.append(stuff)123...124>>> import sys125>>> sys.stdout = StdoutCatcher()126>>> print('foo')127>>> print('hello world!')128>>> sys.stderr.write(''.join(sys.stdout.data))129foo130hello world!131```132133## [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/3.3/faq/extending.html#id12)134135Указатель на объект модуля можно получить следующим образом:136137```c138module = PyImport_ImportModule("<modulename>");139```140141Если модуль ещё не был импортирован (то есть отсутствует в [`sys.modules`](https://python-all.ru/3.3/library/sys.html#sys.modules)), эта функция инициализирует модуль; в противном случае она просто возвращает значение `sys.modules["<имя_модуля>"]`. Обратите внимание: она не добавляет модуль ни в одно пространство имён – она только гарантирует, что модуль инициализирован и сохранён в [`sys.modules`](https://python-all.ru/3.3/library/sys.html#sys.modules).142143Затем можно обратиться к атрибутам модуля (т.е. к любым именам, определённым в модуле) следующим образом:144145```c146attr = PyObject_GetAttrString(module, "<attrname>");147```148149Вызов [`PyObject_SetAttrString()`](https://python-all.ru/3.3/c-api/object.html#PyObject_SetAttrString) для присваивания переменным модуля также работает.150151## [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/3.3/faq/extending.html#id13)152153В зависимости от требований существует множество подходов. Для ручного выполнения начните с чтения [*документа «Extending and Embedding»*](https://python-all.ru/3.3/extending/index.html#extending-index). Следует понимать, что для системы времени выполнения Python нет большой разницы между C и C++ – поэтому стратегия создания нового типа Python на основе C-структуры (указателя) будет работать и для объектов C++.154155Для библиотек C++ см. [*Писать на C сложно; есть ли альтернативы?*](https://python-all.ru/3.3/faq/extending.html#c-wrapper-software).156157## [Я добавил модуль через файл Setup, но make завершается ошибкой; почему?](https://python-all.ru/3.3/faq/extending.html#id14)158159Файл Setup должен заканчиваться символом новой строки; если его нет, сборка завершается ошибкой. (Исправление требует уродливых ухищрений в shell-скриптах, и этот баг настолько незначителен, что не стоит затраченных усилий.)160161## [Как отлаживать расширение?](https://python-all.ru/3.3/faq/extending.html#id15)162163При использовании GDB с динамически загружаемыми расширениями невозможно установить точку останова в расширении до его загрузки.164165В файл `.gdbinit` (или в интерактивном режиме) добавьте команду:166167```c168br _PyImport_LoadDynamicModule169```170171Затем при запуске GDB:172173```c174$ gdb /local/bin/python175gdb) run myscript.py176gdb) continue # repeat until your extension is loaded177gdb) finish   # so that your extension is loaded178gdb) br myfunction.c:50179gdb) continue180```181182## [Хочу скомпилировать модуль Python в своей системе Linux, но не хватает некоторых файлов. Почему?](https://python-all.ru/3.3/faq/extending.html#id16)183184Большинство пакетных версий Python не включают каталог `/usr/lib/python2.x/config/`, который содержит различные файлы, необходимые для компиляции расширений Python.185186Для Red Hat установите пакет python-devel RPM, чтобы получить необходимые файлы.187188Для Debian выполните `apt-get install python-dev`.189190## [Что означает сообщение «SystemError: \_PyImport\_FixupExtension: module yourmodule not loaded»?](https://python-all.ru/3.3/faq/extending.html#id17)191192Это означает, что вы создали модуль расширения с именем «yourmodule», но ваша функция инициализации модуля не выполняет инициализацию с этим именем.193194Каждая функция инициализации модуля будет содержать строку, подобную этой:195196```c197module = Py_InitModule("yourmodule", yourmodule_functions);198```199200Если строка, переданная этой функции, не совпадает с именем вашего модуля расширения, будет возбуждено исключение [`SystemError`](https://python-all.ru/3.3/library/exceptions.html#SystemError).201202## [Как отличить «неполный ввод» от «недопустимого ввода»?](https://python-all.ru/3.3/faq/extending.html#id18)203204Иногда требуется эмулировать поведение интерактивного интерпретатора Python, где выдаётся приглашение продолжения ввода, когда ввод неполон (например, напечатано начало инструкции «if» или не закрыты скобки или тройные кавычки), но при недопустимом вводе сразу выдаётся сообщение об ошибке синтаксиса.205206В Python можно использовать модуль [`codeop`](https://python-all.ru/3.3/library/codeop.html#module-codeop), который достаточно точно приближает поведение парсера. Например, его использует IDLE.207208Самый простой способ сделать это на C – вызвать [`PyRun_InteractiveLoop()`](https://python-all.ru/3.3/c-api/veryhigh.html#PyRun_InteractiveLoop) (возможно, в отдельном потоке) и позволить интерпретатору Python обрабатывать ввод за вас. Также можно установить `PyOS_ReadlineFunctionPointer()` на свою пользовательскую функцию ввода. См. `Modules/readline.c` и `Parser/myreadline.c` для дополнительных советов.209210Однако иногда необходимо запускать встроенный интерпретатор Python в том же потоке, что и остальное приложение, и нельзя позволить [`PyRun_InteractiveLoop()`](https://python-all.ru/3.3/c-api/veryhigh.html#PyRun_InteractiveLoop) останавливаться в ожидании пользовательского ввода. Одно из решений – вызвать `PyParser_ParseString()` и проверить, равно ли `e.error` значению `E_EOF`, что означает, что ввод неполон). Вот пример фрагмента кода, не тестировавшийся, основанный на коде Алекса Фарбера:211212```c213#include <Python.h>214#include <node.h>215#include <errcode.h>216#include <grammar.h>217#include <parsetok.h>218#include <compile.h>219220int testcomplete(char *code)221  /* код должен заканчиваться на \n */222  /* возвращает -1 при ошибке, 0 при неполном коде, 1 при завершённом */223{224  node *n;225  perrdetail e;226227  n = PyParser_ParseString(code, &_PyParser_Grammar,228                           Py_file_input, &e);229  if (n == NULL) {230    if (e.error == E_EOF)231      return 0;232    return -1;233  }234235  PyNode_Free(n);236  return 1;237}238```239240Другое решение – попробовать скомпилировать полученную строку с помощью [`Py_CompileString()`](https://python-all.ru/3.3/c-api/veryhigh.html#Py_CompileString). Если она компилируется без ошибок, попробуйте выполнить возвращённый объект кода, вызвав [`PyEval_EvalCode()`](https://python-all.ru/3.3/c-api/veryhigh.html#PyEval_EvalCode). В противном случае сохраните ввод для последующей обработки. Если компиляция завершается неудачей, определите, является ли это ошибкой или просто требуется дополнительный ввод – извлеките строку сообщения из кортежа исключения и сравните её со строкой «unexpected EOF while parsing». Вот полный пример с использованием библиотеки GNU readline (возможно, стоит игнорировать **SIGINT** при вызове readline()):241242```c243#include <stdio.h>244#include <readline.h>245246#include <Python.h>247#include <object.h>248#include <compile.h>249#include <eval.h>250251int main (int argc, char* argv[])252{253  int i, j, done = 0;                          /* длины строки и кода */254  char ps1[] = ">>> ";255  char ps2[] = "... ";256  char *prompt = ps1;257  char *msg, *line, *code = NULL;258  PyObject *src, *glb, *loc;259  PyObject *exc, *val, *trb, *obj, *dum;260261  Py_Initialize ();262  loc = PyDict_New ();263  glb = PyDict_New ();264  PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());265266  while (!done)267  {268    line = readline (prompt);269270    if (NULL == line)                          /* нажата CTRL-D */271    {272      done = 1;273    }274    else275    {276      i = strlen (line);277278      if (i > 0)279        add_history (line);                    /* сохранять непустые строки */280281      if (NULL == code)                        /* в коде пока ничего нет */282        j = 0;283      else284        j = strlen (code);285286      code = realloc (code, i + j + 2);287      if (NULL == code)                        /* недостаточно памяти */288        exit (1);289290      if (0 == j)                              /* код был пуст, поэтому */291        code[0] = '\0';                        /* чтобы strncat работал корректно */292293      strncat (code, line, i);                 /* добавить строку к коду */294      code[i + j] = '\n';                      /* добавить '\n' к коду */295      code[i + j + 1] = '\0';296297      src = Py_CompileString (code, "<stdin>", Py_single_input);298299      if (NULL != src)                         /* скомпилировано успешно - */300      {301        if (ps1  == prompt ||                  /* ">>> " или */302            '\n' == code[i + j - 1])           /* "... " и двойной '\n' */303        {                                               /* поэтому выполнить его */304          dum = PyEval_EvalCode (src, glb, loc);305          Py_XDECREF (dum);306          Py_XDECREF (src);307          free (code);308          code = NULL;309          if (PyErr_Occurred ())310            PyErr_Print ();311          prompt = ps1;312        }313      }                                        /* синтаксическая ошибка или E_EOF? */314      else if (PyErr_ExceptionMatches (PyExc_SyntaxError))315      {316        PyErr_Fetch (&exc, &val, &trb);        /* сбрасывает исключение! */317318        if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&319            !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */320        {321          Py_XDECREF (exc);322          Py_XDECREF (val);323          Py_XDECREF (trb);324          prompt = ps2;325        }326        else                                   /* какая-то другая синтаксическая ошибка */327        {328          PyErr_Restore (exc, val, trb);329          PyErr_Print ();330          free (code);331          code = NULL;332          prompt = ps1;333        }334      }335      else                                     /* какая-то не-синтаксическая ошибка */336      {337        PyErr_Print ();338        free (code);339        code = NULL;340        prompt = ps1;341      }342343      free (line);344    }345  }346347  Py_XDECREF(glb);348  Py_XDECREF(loc);349  Py_Finalize();350  exit(0);351}352```353354## [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/3.3/faq/extending.html#id19)355356Для динамической загрузки модулей расширения g++ необходимо перекомпилировать Python, перелинковать его с помощью g++ (измените LINKCC в Makefile модулей Python) и слинковать ваш модуль расширения с помощью g++ (например, `g++ -shared -o mymodule.so mymodule.o`).357358## [Можно ли создать класс объектов, в котором одни методы реализованы на C, а другие – на Python (например, через наследование)?](https://python-all.ru/3.3/faq/extending.html#id20)359360В Python 2.2 можно наследовать от встроенных классов, таких как [`int`](https://python-all.ru/3.3/library/functions.html#int), [`list`](https://python-all.ru/3.3/library/stdtypes.html#list), [`dict`](https://python-all.ru/3.3/library/stdtypes.html#dict) и т.д.361362Библиотека Boost.Python (BPL, [http://www.boost.org/libs/python/doc/index.html](https://python-all.ru/3.3/faq/extending.html)) предоставляет способ сделать это из C++ (т.е. вы можете наследоваться от класса расширения, написанного на C++, с помощью BPL).363