У меня есть приложение C++/Qt, в котором я запускаю сеанс интерпретатора Python в процессе основного приложения. Я создал «консоль Python» в виде виджета QPlainTextEdit
, который обрабатывает ввод и вывод для интерпретатора с помощью API Python C. Смысл всего этого в том, что Python будет иметь прямой доступ к данным, которые я храню внутри основного приложения. Все хорошо, за исключением того, что я хочу иметь возможность завершить текущий сеанс интерпретатора и перезапустить его, не выходя из основного приложения.
В настоящее время я пробую очевидный способ:
Py_Initialize();
// Run the main session...
Py_FinalizeEx();
// Restart the session
Py_Initialize();
Судя по другим сообщениям и документации Python, могут возникнуть проблемы с перезагрузкой определенных модулей после завершения работы интерпретатора из-за утечек памяти. В моем случае это похоже на правду: повторный импорт определенных модулей (например, «numpy»), но не других (например, «sys») вызовет исключение и завершится ошибкой.
Существует ли хорошая стратегия обхода, позволяющая перезапустить интерпретатор без этих проблем? Например, если я создаю субинтерпретатор, возникнут ли при его завершении и перезапуске нового субинтерпретатора те же проблемы, что и у меня? Альтернатива, которой я пытался избежать, — это запуск Python вне процесса, что, я думаю, позволило бы мне перезапустить его, по сути убив процесс и запустив новый. Любые советы по стратегии будут высоко оценены.
Почему вы хотите завершить текущий сеанс переводчика и перезапустить его? Кажется, это XY проблема.
У вас есть API подинтерпретаторов, если вы хотите, чтобы в вашем приложении было несколько интерпретаторов, их можно безопасно создавать и уничтожать.
Numpy — это именно тот проблемный дочерний элемент, который нельзя загружать несколько раз в одном процессе.
@ChukwujiobiCanon В идеале у пользователя должна быть возможность изменить среду Python, которую использует основное приложение. Кроме того, основное приложение представляет собой пакет анализа данных, поэтому пользователи уже могут начать новый файл/сеанс анализа из меню «Файл», не выходя из приложения. В этом случае они также ожидают нового сеанса Python.
@BenMurphy-Baum, в этом случае запуск Python как внешнего процесса кажется мне лучшим решением.
Из документации Py_FinalizeEx:
Ошибки и предостережения: Уничтожение модулей и объектов в модулях делается в случайном порядке; это может вызвать деструкторы (методы del()) потерпеть неудачу, когда они зависят от других объектов (даже функций) или модулей. Динамически загружаемые модули расширения, загружаемые Python, не выгружен. Небольшие объемы памяти, выделяемые интерпретатором Python не может быть освобожден (если вы обнаружите утечку, сообщите об этом). Память привязана up в циклических ссылках между объектами не освобождается. Немного памяти выделенные модулями расширения не могут быть освобождены. Некоторые расширения могут не работают должным образом, если их процедура инициализации вызывается более чем один раз; это может произойти, если приложение вызывает Py_Initialize() и Py_FinalizeEx() более одного раза.
Особо отметим, что:
Некоторые расширения могут работать неправильно, если их процедура инициализации вызывается более чем один раз; это может произойти, если приложение вызывает Py_Initialize() и Py_FinalizeEx() более одного раза.
(выделено мной)
Понятно, что проект не поддерживает финализацию и повторную инициализацию при попытке.
Поэтому рекомендуется инициализировать движок Python один раз (при запуске процесса или в первый раз, когда он вам понадобится) и завершать его только при выходе (или, по крайней мере, когда вы уверены, что он вам больше не понадобится).
Даже если вы найдете обходной путь, который будет работать в конкретном сценарии, я не думаю, что это хорошая идея, учитывая приведенную выше информацию.
Примечание:
Если у вас есть какие-либо требования, требующие создания новой среды Python для каждого сеанса или операции, лучший подход — запустить Python как внешний процесс.
Почему необходимо завершить и повторно инициализировать? Разве вы не можете инициализировать один раз и завершить только при выходе?