Вопрос-Ответ

How do I unload (reload) a Python module?

Как мне выгрузить (перезагрузить) модуль Python?

У меня давно работающий сервер Python, и я хотел бы иметь возможность обновлять службу без перезапуска сервера. Какой наилучший способ сделать это?

if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Переведено автоматически
Ответ 1

Вы можете перезагрузить модуль, когда он уже был импортирован, используя importlib.reload():

from importlib import reload  # Python 3.4+
import foo

while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)

В Python 2 reload был встроен. В Python 3 он был перемещен в imp модуль. В версии 3.4 imp был устаревшим в пользу importlib. При настройке версии 3 или более поздней либо ссылайтесь на соответствующий модуль при вызовеreload, либо импортируйте его.

Я думаю, что это то, что вы хотите. Веб-серверы, такие как сервер разработки Django, используют это, чтобы вы могли видеть последствия изменений вашего кода без перезапуска самого серверного процесса.

Цитирую по документам:



  • Код модуля Python перекомпилируется и код уровня модуля выполняется повторно, определяя новый набор объектов, которые привязаны к именам в словаре модуля, путем повторного использования загрузчика, который изначально загрузил модуль. init Функция модулей расширения не вызывается во второй раз.

  • Как и в случае со всеми другими объектами в Python, старые объекты восстанавливаются только после того, как количество их ссылок падает до нуля.

  • Имена в пространстве имен модуля обновляются, чтобы указывать на любые новые или измененные объекты.

  • Другие ссылки на старые объекты (например, внешние по отношению к модулю имена) не восстанавливаются для ссылки на новые объекты и должны быть обновлены в каждом пространстве имен, где они встречаются, если это необходимо.


Как вы отметили в своем вопросе, вам придется реконструировать Foo объекты, если Foo класс находится в foo модуле.

Ответ 2

В Python 3.0–3.3 вы бы использовали: imp.reload(module)

В BDFL есть ответ на этот вопрос.

Однако, imp устарел в версии 3.4 в пользу importlib (спасибо @Stefan!).

Я думаю, поэтому вы бы сейчас использовали importlib.reload(module), хотя я не уверен.

Ответ 3

Удалить модуль, если это не чистый Python, может быть особенно сложно.

Вот некоторая информация из: Как мне действительно удалить импортированный модуль?


Вы можете использовать sys.getrefcount(), чтобы узнать фактическое количество ссылок.


>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3

Числа больше 3 указывают на то, что
избавиться от модуля будет сложно
. Доморощенный "пустой"
(ничего не содержащий) модуль должен быть
собран мусор после


>>> del sys.modules["empty"]
>>> del empty

в качестве третьей ссылки используется артефакт
функции getrefcount().


Ответ 4

reload(module), но только если он полностью автономный. Если что-либо еще имеет ссылку на модуль (или любой объект, принадлежащий модулю), то вы получите тонкие и любопытные ошибки, вызванные тем, что старый код висит дольше, чем вы ожидали, и такие вещи, как isinstance не работает в разных версиях одного и того же кода.

Если у вас односторонние зависимости, вы также должны перезагрузить все модули, которые зависят от перезагруженного модуля, чтобы избавиться от всех ссылок на старый код. А затем рекурсивно перезагрузить модули, которые зависят от перезагруженных модулей.

Если у вас есть циклические зависимости, что очень распространено, например, когда вы имеете дело с перезагрузкой пакета, вы должны выгрузить все модули в группе за один раз. Вы не можете этого сделать с помощью reload(), потому что он повторно импортирует каждый модуль до обновления его зависимостей, позволяя старым ссылкам проникать в новые модули.

Единственный способ сделать это в данном случае - взломать sys.modules, который отчасти не поддерживается. Вам пришлось бы просмотреть и удалить каждую sys.modules запись, которую вы хотели бы перезагрузить при следующем импорте, а также удалить записи, значения которых None чтобы решить проблему реализации, связанную с кэшированием неудачного относительного импорта. Это не очень приятно, но пока у вас есть полностью автономный набор зависимостей, который не оставляет ссылок за пределами своей кодовой базы, это выполнимо.

Вероятно, лучше всего перезапустить сервер. :-)

2023-07-19 13:58 python