Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Пётр Андреев (МФТИ, лектор по курсу Advanced Py...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Пётр Андреев (МФТИ, лектор по курсу Advanced Python). Дебри Python или как работает повседневный Python: что происходит на самом деле

Что происходит, когда вы запускаете CPython-скрипт, и как он запускается на C? Разберём, как код превращается в байткод: все этапы и C-реализация. GIL: обсудим более подробно в сборках с GIL.

Видео: https://moscowpython.ru/meetup/102/python-insides/

Moscow Python: http://moscowpython.ru
Курсы Learn Python: http://learn.python.ru
Moscow Python Podcast: http://podcast.python.ru
Заявки на доклады: https://bit.ly/mp-speaker

Avatar for Moscow Python Meetup

Moscow Python Meetup PRO

June 24, 2025

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Agenda 2 Дебри Python. Начало. автор tg: @PyotrAndreev • Byte-code

    • Что происходит, когда вы запускаете CPython-скрипт • GIL
  2. Byte-code 3 Что это? автор tg: @PyotrAndreev def main(): #

    строка 1 a = 10 # строка 2 b = 20 # строка 3 return a + b # строка 4 Какой-то код “If the implementation is hard to explain, it's a bad idea.” – Zen CPython
  3. Byte-code 4 Что это? автор tg: @PyotrAndreev def main(): #

    строка 1 a = 10 # строка 2 b = 20 # строка 3 return a + b # строка 4 Множество CPU: - инструкции - архитектура OS: - реализации Какой-то код x86-64 ARM RISC-V Linux Windows www.python.org
  4. Byte-code 5 Что это? автор tg: @PyotrAndreev def main(): #

    строка 1 a = 10 # строка 2 b = 20 # строка 3 return a + b # строка 4 Множество CPU: - инструкции - архитектура OS: - реализации Какой-то код x86-64 ARM RISC-V Linux Windows >>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. …
  5. Byte-code 6 Что это? автор tg: @PyotrAndreev def main(): #

    строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 Какой-то код x86-64 ARM RISC-V Linux Windows PVM “Simple is better than complex.” – Zen CPython Больше абстракций
  6. Byte-code 7 Что это? автор tg: @PyotrAndreev def main(): #

    строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 Какой-то код x86-64 ARM RISC-V Linux Windows PVM “Simple is better than complex.” – Zen CPython Больше абстракций Больше абстракций ***.py ***.pyc Byte-code ***.pyc – отображение исходного кода через ограниченный набор opt.codes (operation code) Хранится в __pycache__
  7. Byte-code 8 Что это? автор tg: @PyotrAndreev def main(): #

    строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 Какой-то код x86-64 ARM RISC-V Linux Windows PVM “Simple is better than complex.” – Zen CPython Больше абстракций Больше абстракций ***.pyc ***.py ***.pyc – отображение исходного кода через ограниченный набор opt.codes (operation code) Хранится в __pycache__ 3.14.0b2 [GCC 13.3.0] >>> import dis >>> len(dis.opnames) 267 Что за множество? Byte-code
  8. Byte-code 9 Задача преобразования кода автор tg: @PyotrAndreev def main():

    # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 ? Какой-то код ***.pyc ***.py Byte-code doc: opt.code GitHub: opt.code
  9. Byte-code 10 Задача преобразования кода автор tg: @PyotrAndreev def main():

    # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 ? Какой-то код ***.pyc ***.py Byte-code Вид ***.pyc 3.14.0b2 [GCC 13.3.0] >>> main.__code__ <code object main at 0x76b0916b9c50, … > types.CodeType 3.14.0b2 [GCC 13.3.0] >>> main.__code__.co_code b'\x80\x00^\x05p\x00^\np\x01W\x01,\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00' Последовательность opt.codes с аргументами, за которыми стоит уже С-код doc: opt.code GitHub: opt.code Диспатчинг от PVM: _PyEval_EvalFrameDefault: • читает byte-code • по opt.code находит соответствующий C-код • вызывает его • управляет стеком, лок.переменными … • возвращает результат: PyObject* или NULL
  10. Byte-code 11 Задача преобразования кода автор tg: @PyotrAndreev def main():

    # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 ? Какой-то код ***.pyc ***.py Byte-code doc: opt.code GitHub: opt.code В человеко-читабельном виде: >>> from dis import dis >>> dis(main)
  11. Byte-code 12 Задача преобразования кода автор tg: @PyotrAndreev doc: opt.code

    GitHub: opt.code 1 0 RESUME 0 2 2 LOAD_CONST 1 (5) 4 STORE_FAST 0 (a) 3 6 LOAD_CONST 2 (10) 8 STORE_FAST 1 (b) 4 10 LOAD_FAST 0 (a) 12 LOAD_FAST 1 (b) 14 BINARY_OP 0 (+) 18 RETURN_VALUE Инициализация: (>= Python 3.11) При вызове ф-ии _PyInterpreterFrame /+InternalDocs/ создаёт/переиспользует фрейм, инструкция RESUME 0 помогает инициализировать его для opt.codes Загружает константу (5) из таблицы констант ф-и main.__code__.co_consts и помещает ссылку на неё в стек Из вершины стека извлекается значение (ссылка на объект 5) и сохраняется в локальной переменной a. Локальные переменные хранятся в: main.__code__.co_varnames Индекс 0 -> переменная a в списке локальных переменных ф-ии Загружает значение лок.переменной a (ссылка на 5) на стек Бинарная операция над 2 верхними элементами стека (ссылки на 5 и на 10) Результат: новый объект (число 15) созданный в куче Результатом является новый объект (число 15), созданный в куче, ссылка который помещается обратно в стек. Извлекает значение с вершины стека и возвращает его как результат работы функции. номера строк исходного кода # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4
  12. Byte-code 13 Задача преобразования кода автор tg: @PyotrAndreev doc: opt.code

    GitHub: opt.code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 14 BINARY_OP 0 (+) 18 RETURN_VALUE 1. число 15 не создаётся 2. Что такое 0 (+) ?
  13. Byte-code 14 Задача преобразования кода автор tg: @PyotrAndreev doc: opt.code

    GitHub: opt.code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 14 BINARY_OP 0 (+) 18 RETURN_VALUE 1. число 15 не создаётся 2. Что такое 0 (+) ? family(BINARY_OP, INLINE_CACHE_ENTRIES_BINARY_OP) = { BINARY_OP_MULTIPLY_INT, BINARY_OP_ADD_INT, BINARY_OP_SUBTRACT_INT, BINARY_OP_MULTIPLY_FLOAT, BINARY_OP_ADD_FLOAT, ... }; CPython 3.15.0а0 GitHub: версия, семейство BINARY_OP: неспециализированная операция С 3.11 Specializing Adaptive Interpreter: PEP 659 INLINE_CACHE_ENTRIES_BINARY_OP (=5): число нулевых opt.codes (CACHE), которые стоят перед BINARY_OP
  14. Byte-code 15 Задача преобразования кода автор tg: @PyotrAndreev doc: opt.code

    GitHub: opt.code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 14 BINARY_OP 0 (+) 18 RETURN_VALUE 1. число 15 не создаётся 2. Что такое 0 (+) ? family(BINARY_OP, INLINE_CACHE_ENTRIES_BINARY_OP) = { BINARY_OP_MULTIPLY_INT, BINARY_OP_ADD_INT, BINARY_OP_SUBTRACT_INT, BINARY_OP_MULTIPLY_FLOAT, BINARY_OP_ADD_FLOAT, ... }; CPython 3.15.0а0 GitHub: версия, семейство BINARY_OP: неспециализированная операция С 3.11 Specializing Adaptive Interpreter: PEP 659 INLINE_CACHE_ENTRIES_BINARY_OP (=5): число нулевых opt.codes (CACHE), которые стоят перед BINARY_OP 12 CACHE # slot0 = –1 (защитный счётчик) 14 CACHE # slot1 = &PyLong_Type 16 CACHE # slot2 = &PyLong_Type 18 CACHE # slot3 = &fast_int_add 20 CACHE # slot4 = резерв (для будущего) 22 BINARY_OP_ADD_INT 5 12 CACHE # slot0 = 8 (счётчик) 14 CACHE # slot1 = None 16 CACHE # slot2 = None 18 CACHE # slot3 = None 20 CACHE # slot4 – резерв 22 BINARY_OP 5 # PyNumber_Add – медленно При промаха счётчик уменьшается; при пробитии порога → откат Byte-code
  15. Byte-code 16 Задача преобразования кода автор tg: @PyotrAndreev ? Какой-то

    код ***.pyc ***.py Byte-code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4
  16. Byte-code 17 Задача преобразования кода автор tg: @PyotrAndreev ? Какой-то

    код ***.pyc ***.py Byte-code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 1. Lexer токенизирует код: tokenize _PyTokenizer_Get (часть лексера) 2. Parser строит построение AST Abstract Syntax Trees: ast _PyPegen_ASTFromString 3. построение символьной таблицы: PySymtable_BuildObject ◦ обходит AST, помечает имена флагами local/global/cell/free ◦ записи в co_varnames/co_cellvars/co_freevars
  17. Byte-code 18 Задача преобразования кода автор tg: @PyotrAndreev ? Какой-то

    код ***.pyc ***.py Byte-code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 1. Lexer токенизирует код: tokenize 2. Parser строит построение AST Abstract Syntax Trees: ast 3. построение символьной таблицы a = 5 b = 10 c = a + b print(c) Пример
  18. Byte-code 19 Задача преобразования кода автор tg: @PyotrAndreev ? Какой-то

    код ***.pyc ***.py Byte-code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 1. Lexer токенизирует код: tokenize 2. Parser строит построение AST Abstract Syntax Trees: ast 3. построение символьной таблицы a (идентификатор), = (оператор присваивания), 5 (целое число), b (идентификатор), = (оператор присваивания), 10 (целое число), c (идентификатор), = (оператор присваивания), a (идентификатор), + (оператор сложения), b (идентификатор), print (функция), ( (открывающая скобка), c (идентификатор), ) (закрывающая скобка) a = 5 b = 10 c = a + b print(c) Пример Токены
  19. Byte-code 20 Задача преобразования кода автор tg: @PyotrAndreev ? Какой-то

    код ***.pyc ***.py Byte-code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 1. Lexer токенизирует код: tokenize 2. Parser строит построение AST Abstract Syntax Trees: ast 3. построение символьной таблицы a (идентификатор), = (оператор присваивания), 5 (целое число), b (идентификатор), = (оператор присваивания), 10 (целое число), c (идентификатор), = (оператор присваивания), a (идентификатор), + (оператор сложения), b (идентификатор), print (функция), ( (открывающая скобка), c (идентификатор), ) (закрывающая скобка) Assign (присваивание) ├── Name (a) └── Constant (5) Assign (присваивание) ├── Name (b) └── Constant (10) Assign (присваивание) ├── Name (c) └── BinOp (бинарная операция) ├── Name (a) └── Name (b) Call (вызов функции) ├── Name (print) └── Name (c) a = 5 b = 10 c = a + b print(c) Пример Токены Иерархическое дерево: • последовательность выполнения • отношения между объектами
  20. Byte-code 21 Задача преобразования кода автор tg: @PyotrAndreev ? Какой-то

    код ***.pyc ***.py Byte-code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4
  21. Byte-code 22 Задача преобразования кода автор tg: @PyotrAndreev ✔ Какой-то

    код ***.pyc ***.py Byte-code # CPython 3.11.13 [GCC 11.4.0] def main(): # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4
  22. Byte-code 23 Задача преобразования кода автор tg: @PyotrAndreev def main():

    # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 Какой-то код x86-64 ARM RISC-V Linux Windows PVM “Simple is better than complex.” – Zen CPython Больше абстракций Больше абстракций ***.py ***.pyc Byte-code
  23. Запуск программы 24 Немного исходников автор tg: @PyotrAndreev def main():

    # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 Какой-то код x86-64 ARM RISC-V Linux Windows PVM “Simple is better than complex.” – Zen CPython Больше абстракций Больше абстракций ***.py ***.pyc Byte-code Этапы запуска CPython
  24. Запуск программы 25 Немного исходников автор tg: @PyotrAndreev def main():

    # строка 1 a = 5 # строка 2 b = 10 # строка 3 return a + b # строка 4 Какой-то код x86-64 ARM RISC-V Linux Windows PVM “Simple is better than complex.” – Zen CPython Больше абстракций Больше абстракций ***.py ***.pyc Byte-code Этапы запуска CPython user@comp:~$ python3 foo.py ... • Unix находит исполняемый файл (ИФ): /usr/bin/python3, который содержит ф-ю main из Programs/python.c ... • ИФ вызывает main, что вызывает цепочку вызовов из Modules/main.c: Py_BytesMain → pymain_main → Py_RunMain → запуск главного цикла выполнения Python‑кода
  25. Запуск программы 26 Немного исходников автор tg: @PyotrAndreev main (Programs/python.c)

    │ └> Py_BytesMain(argc, argv) (Modules/main.c) │ └> pymain_main(&args) │ ├> status = pymain_init(args) # Инициализация интерпретатора │ └── (настройка PyConfig, путей, сигналов и т.д.) │ ├> Проверка статуса (если требуется завершение или ошибка) │ └> Py_RunMain() │ ├> pymain_run_python(&exitcode) │ ├> Обновление конфигурации и настройка путей │ ├> Выбор режима выполнения: │ │ • Если задана команда → pymain_run_command() │ │ • Если задан модуль → pymain_run_module() │ │ • Если задан файл → pymain_run_file() │ │ • Иначе чтение из stdin → pymain_run_stdin() │ └> pymain_repl(config, exitcode) # Запуск REPL (интерактивного режима) │ ├> Py_FinalizeEx() # Завершение интерпретатора │ └> pymain_free() # Освобождение глобальных ресурсов Этапы запуска CPython
  26. Byte-code 27 Задача преобразования кода автор tg: @PyotrAndreev run.py byte-code

    исполнение PVM Компьютер процессы -> потоки Запуск отдельного процесса под PVM исполнение файла Lexer -> токенизация Parser -> AST ... a = 5 b = 10 c = a + b print(c) a (идентификатор), = (оператор присваивания), 5 (целое число), b (идентификатор), = (оператор присваивания), 10 (целое число), c (идентификатор), = (оператор присваивания), a (идентификатор), + (оператор сложения), b (идентификатор), print (функция), ( (открывающая скобка), c (идентификатор),) (закрывающая скобка) Assign (присваивание) ├── Name (a) └── Constant (5) Assign (присваивание) ├── Name (b) └── Constant (10) Assign (присваивание) ├── Name (c) └── BinOp (бинарная операция) ├── Name (a) └── Name (b) Call (вызов функции) ├── Name (print) └── Name (c) b'\x97\x00d\x01}\x00d\x02}\x01|\x00|\x01z\x00\x00 \x00}\x02t\x01\x00\x00\x00\x00\x00\x00\x00\x00|\x 02\xab\x01\x00\x00\x00\x00\x00\x00\x01\x00y\x00' 0 RESUME 2 LOAD_CONST 4 STORE_FAST 6 LOAD_CONST 8 STORE_FAST 10 LOAD_FAST 12 LOAD_FAST 14 BINARY_OP 18 STORE_FAST 20 LOAD_GLOBAL 30 LOAD_FAST 32 CALL 40 POP_TOP 42 RETURN_CONST AST byte-code (.pyc) byte-code foo.py ‘дёргает’ Python C API
  27. Processes: • отдельное адресное пространство (виртуальная память) • изменения в

    памяти одного процесса не влияют на другие • обход GIL: модуль multiprocessing 28 Исполнение кода автор tg: @PyotrAndreev Threads: threading – интерфейс для работы • внутри процесса делят общее адресное пространство, но не стек • создание и переключение быстрее, чем между процессами • GIL threads & processes
  28. 29 автор tg: @PyotrAndreev threads & processes Что хотим? •

    Получить максимальную эффективность от CPU-bound задачах передаваемые данные необходимо сериализовать: дорого Processes: • отдельное адресное пространство (виртуальная память) • изменения в памяти одного процесса не влияют на другие • обход GIL: модуль multiprocessing Коммуникация между • Queues, Pipes, Managers(общие объекты) • Shared Memory – необходимы инструменты синхронизации, чтобы избежать race condition Threads: threading – интерфейс для работы • внутри процесса делят общее адресное пространство, но не стек • создание и переключение быстрее, чем между процессами • GIL Коммуникация между • Lock, RLock: блокировки для защиты критических секций • Condition, Semaphore, Event, Barrier ... pickle Исполнение кода
  29. 30 автор tg: @PyotrAndreev GIL (CPython) “Для … вычислительных задач

    … недостаток параллелизма … является большей проблемой, чем скорость выполнения кода Python…” “The GIL … a global bottleneck” типы параллелизма: arxiv timemodule.c: repo
  30. автор tg: @PyotrAndreev GIL (CPython) “Для … вычислительных задач …

    недостаток параллелизма … является большей проблемой, чем скорость выполнения кода Python…” “The GIL … a global bottleneck” 31 типы параллелизма: arxiv • гарантирует, что одновременно выполняется PVM только один поток байт-кода (-> Python C API) -> что обеспечивает потокобезопасность • Защищает race conditions на Python уровне, но не глубже: ◦ C-расширения могут предоставлять параллелизм в процессе timemodule.c: repo
  31. автор tg: @PyotrAndreev GIL (CPython) Как передаётся GIL? макросами •

    Py_BEGIN_ALLOW_THREADS освобождает GIL Py_END_ALLOW_THREADS захватывает GIL • GIL не будет захвачен другими потоками, если им это не требуется “Для … вычислительных задач … недостаток параллелизма … является большей проблемой, чем скорость выполнения кода Python…” “The GIL … a global bottleneck” 32 типы параллелизма: arxiv • гарантирует, что одновременно выполняется PVM только один поток байт-кода (-> Python C API) -> что обеспечивает потокобезопасность • Защищает race conditions на Python уровне, но не глубже: ◦ C-расширения могут предоставлять параллелизм в процессе time.sleep(10) timemodule.c: repo
  32. автор tg: @PyotrAndreev Пример: • исполняется внутренняя С-функция: time_sleep •

    ... • вычисляется время до, которого спим • используем Py_BEGIN_ALLOW_THREADS -> сохраняет состояние треда: PyEval_SaveThread -> освобождает GIL • вызывается OS блокирующая операция для усыпления потока на заданное время • OS пробуждает поток по истечению времени • другие потоки могут выполнять Python байт-код • используем Py_END_ALLOW_THREADS -> восстанавливаем состояние потока: PyEval_RestoreThread -> захват GIL потоком GIL (CPython) Как передаётся GIL? макросами • Py_BEGIN_ALLOW_THREADS освобождает GIL Py_END_ALLOW_THREADS захватывает GIL • GIL не будет захвачен другими потоками, если им это не требуется “Для … вычислительных задач … недостаток параллелизма … является большей проблемой, чем скорость выполнения кода Python…” “The GIL … a global bottleneck” 33 типы параллелизма: arxiv • гарантирует, что одновременно выполняется PVM только один поток байт-кода (-> Python C API) -> что обеспечивает потокобезопасность • Защищает race conditions на Python уровне, но не глубже: ◦ C-расширения могут предоставлять параллелизм в процессе time.sleep(10) timemodule.c: repo
  33. автор tg: @PyotrAndreev Пример: • исполняется внутренняя С-функция: time_sleep •

    ... • вычисляется время до, которого спим • используем Py_BEGIN_ALLOW_THREADS -> сохраняет состояние треда: PyEval_SaveThread -> освобождает GIL • вызывается OS блокирующая операция для усыпления потока на заданное время • OS пробуждает поток по истечению времени • другие потоки могут выполнять Python байт-код • используем Py_END_ALLOW_THREADS -> восстанавливаем состояние потока: PyEval_RestoreThread -> захват GIL потоком GIL (CPython) Как передаётся GIL? макросами • Py_BEGIN_ALLOW_THREADS освобождает GIL Py_END_ALLOW_THREADS захватывает GIL • GIL не будет захвачен другими потоками, если им это не требуется “Для … вычислительных задач … недостаток параллелизма … является большей проблемой, чем скорость выполнения кода Python…” “The GIL … a global bottleneck” 34 типы параллелизма: arxiv • гарантирует, что одновременно выполняется PVM только один поток байт-кода (-> Python C API) -> что обеспечивает потокобезопасность • Защищает race conditions на Python уровне, но не глубже: ◦ C-расширения могут предоставлять параллелизм в процессе time.sleep(10) timemodule.c: repo { Py_BEGIN_ALLOW_THREADS /* your code here */ Py_END_ALLOW_THREADS } Разворачивается в: { PyThreadState *_save; _save = PyEval_SaveThread(); /* your code here */ PyEval_RestoreThread(_save); } что есть:
  34. автор tg: @PyotrAndreev Пример: • исполняется внутренняя С-функция: time_sleep •

    ... • вычисляется время до, которого спим • используем Py_BEGIN_ALLOW_THREADS -> сохраняет состояние треда: PyEval_SaveThread -> освобождает GIL • вызывается OS блокирующая операция для усыпления потока на заданное время • OS пробуждает поток по истечению времени • другие потоки могут выполнять Python байт-код • используем Py_END_ALLOW_THREADS -> восстанавливаем состояние потока: PyEval_RestoreThread -> захват GIL потоком GIL (CPython) Как передаётся GIL? макросами • Py_BEGIN_ALLOW_THREADS освобождает GIL Py_END_ALLOW_THREADS захватывает GIL • GIL не будет захвачен другими потоками, если им это не требуется “Для … вычислительных задач … недостаток параллелизма … является большей проблемой, чем скорость выполнения кода Python…” “The GIL … a global bottleneck” 35 типы параллелизма: arxiv Преимущества GIL: • Более быстрая разработка • Простой подсчёт ссылок через: Py_IncRef, Py_DecRef • ... • гарантирует, что одновременно выполняется PVM только один поток байт-кода (-> Python C API) -> что обеспечивает потокобезопасность • Защищает race conditions на Python уровне, но не глубже: ◦ C-расширения могут предоставлять параллелизм в процессе time.sleep(10) timemodule.c: repo { Py_BEGIN_ALLOW_THREADS /* your code here */ Py_END_ALLOW_THREADS } Разворачивается в: { PyThreadState *_save; _save = PyEval_SaveThread(); /* your code here */ PyEval_RestoreThread(_save); } что есть: