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

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

if foo.py has changed:
    unimport foo  <-- How do I do this?
    import foo
    myfoo = foo.Foo()

Подсказка: «импорт» не означает «загрузка», это означает «загрузить, если еще не загружен, а затем импортировать в пространство имен».

Kos 10.01.2013 15:30

вопрос не должен включать `` выгрузку '', поскольку это пока невозможно в python - перезагрузка, однако, является известной парадигмой, как указано ниже

rjmoggach 29.05.2014 08:08

Что делать, если вы хотите «выгрузить», потому что попытка удалить файл .pyc используется кодом?

darkgaze 29.09.2016 16:48

У меня была такая же проблема при использовании динамического модуля в приложении py2exe. Поскольку py2exe всегда сохраняет байт-код в zip-каталоге, перезагрузка не работает. Но я нашел рабочее решение с использованием модуля import_file. Теперь мое приложение работает нормально.

Pritam Pan 06.03.2016 00:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
885
4
704 264
21
Перейти к ответу Данный вопрос помечен как решенный

Ответы 21

Ответ принят как подходящий

Вы можете перезагрузить модуль, если он уже был импортирован, используя встроенную функцию reload(Только Python 3.4+):

from importlib import reload  
import foo

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

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

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

Цитата из документов:

Python modules’ code is recompiled and the module-level code reexecuted, defining a new set of objects which are bound to names in the module’s dictionary. The init function of extension modules is not called a second time. As with all other objects in Python the old objects are only reclaimed after their reference counts drop to zero. The names in the module namespace are updated to point to any new or changed objects. Other references to the old objects (such as names external to the module) are not rebound to refer to the new objects and must be updated in each namespace where they occur if that is desired.

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

на самом деле сервер django dev перезагружается, когда вы меняете файл .. (он перезагружает сервер, а не просто перезагружает модуль)

hasen 13.01.2009 09:36

Ах, вы правы - похоже, они просто перезагружают серверный поток. Ну, просто знайте, что они мог используют перезагрузку, я думаю. :-)

cdleary 16.01.2009 14:55

откуда взялась эта функция is_changed? я не вижу по нему документации, и он не работает в моей среде Python 3.1.3 и не работает в 2.6.4.

jedmao 17.06.2011 18:29

В командной строке iPython reload, похоже, не следует цепочке зависимостей импорта, он только перезагружает модули, указанные в аргументе.

hobs 19.03.2012 10:55

Python 3 изменил его на import imp / imp.reload (foo)

Yuri Astrakhan 05.04.2012 18:35

нет cdleary, Django не может просто использовать перезагрузку: pyunit.sourceforge.net/notes/reloading.html

raylu 05.10.2012 09:54

К вашему сведению, по крайней мере, в Python 2 вы можете просто использовать reload(foo) вместо foo = reload(foo).

John M. 07.02.2013 22:06

Странный. Ответ касается только перезарядки. А как насчет разгрузки?

fatuhoku 16.10.2013 14:58

Как использовать reload для импорта, такого как "from a.b.c import someClass as abcClass" ... reload (abcClass) жалуется, что он не определен.

nmz787 05.09.2014 01:20

перезагрузки недостаточно для модулей с зависимостями. См. Ниже: stackoverflow.com/a/438845/456878. Это уже укусило меня и потратило впустую добрые 10 минут.

Doug Bradshaw 18.02.2015 16:59

Это невозможно для расширений C в Windows, потому что файл .pyd уже используется и не может быть обновлен.

ivan_pozdeev 22.10.2016 15:10

Как лучше всего ответить на этот вопрос? Это не работает в моем py27 и / или py34 @jedmao. Вы когда-нибудь узнали, откуда исходит "is_changed"?

James Draper 13.12.2016 20:35

@JamesDraper нет, так и не узнал, откуда взялся is_changed ... или забыл. Это было так давно.

jedmao 17.12.2016 02:16

К вашему сведению, перезагрузка - источник зла. Например, все экземпляры объектов, созданные с помощью первого импорта, будут ссылаться на старый класс: import foo; bar = foo.Bar(); reload(foo); assert isinstance(bar, foo.Bar) вызовет AssertError

Tiago Coutinho 09.07.2017 10:27

@jedmao @JamesDraper Я почти уверен, что функция is_changed - это просто произвольная функция, которую вам придется написать; это не встроенный. Например, он может открыть файл, соответствующий импортируемому модулю, и сравнить его с кэшированной версией, чтобы увидеть, изменился ли он.

James Mchugh 11.09.2018 17:38

Этот метод может не переопределять ссылки других модулей на перезагруженный модуль. См. stackoverflow.com/a/61617169/2642356 для решения этой проблемы.

EZLearner 05.05.2020 18:57

Я должен спросить. Что будет после компиляции программы?

Jdeep 28.11.2020 21:14

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

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

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

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

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

Разве dreload не подходит специально для этого сценария?

Josh 01.06.2012 01:10

@Josh: нет, это для перезагрузки дерева пакетов, и даже тогда он работает только до тех пор, пока пакет не имеет внешних / циклических зависимостей.

bobince 02.06.2012 14:29

Можете ли вы уточнить часть со значениями None, потому что я столкнулся именно с этой проблемой: я удаляю элементы из sys.modules, и после повторного импорта некоторые импортированные зависимости являются None.

schlamar 26.03.2013 14:15

@shclamar: См. stackoverflow.com/questions/1958417/… (и ссылки оттуда) для справки. Мне неясно (даже глядя на код import.c), как записи None смогли вернуться через механизм импорта, когда «настоящие» записи были удалены, и я не могу этого добиться в 2.7; в будущем это, конечно, больше не проблема, поскольку неявный относительный импорт исчез. Между тем, удаление всех записей со значением None, похоже, решает эту проблему.

bobince 26.03.2013 19:13

для новичков было бы лучше, если бы кто-нибудь мог сослаться на то, откуда или из какой библиотеки взялась функция, в случае этого ответа reload (module)

Eliethesaiyan 20.04.2016 16:18

@Eliethesaiyan: вы имеете в виду функцию reload? Он встроен, вам не нужно импортировать какую-либо библиотеку.

bobince 22.04.2016 17:07

@ bobince, думаю, вы подходите для перезагрузки .. Раньше я использовал его из пакета imp

Eliethesaiyan 27.04.2016 03:57

Я написал функцию ниже, которая заменяет содержимое предыдущего модуля новым и, следовательно, решает проблему, справедливо упомянутую @bobince. См. stackoverflow.com/a/61617169/2642356

EZLearner 05.05.2020 18:46

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

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

You can use sys.getrefcount() to find out the actual number of references.

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

Numbers greater than 3 indicate that it will be hard to get rid of the module. The homegrown "empty" (containing nothing) module should be garbage collected after

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

as the third reference is an artifact of the getrefcount() function.

Я только что обнаружил, что если модуль является частью пакета, вы должны удалить его и там: setattr(package, "empty", None)

u0b34a0f6ae 11.05.2010 16:33

Это правильное решение, особенно если у вас есть пакет с вложенными модулями. reload() перезагружает только самый верхний модуль, и все, что находится внутри него, не будет перезагружено, если вы сначала не удалите его из sys.modules.

Cerin 05.12.2016 23:05

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

БДФЛ имеет ответил этот вопрос.

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

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

Серьезный новичок будет благодарен за то, что узнал о важных нюансах между Python 2 и 3.

Smandoli 30.06.2010 01:26

@ LoïcFaure-Lacroix: хороший вопрос, понятия не имею. (Думаю, да?) Может быть, стоит задать вопрос.

Paul D. Waite 05.11.2011 17:46

@ LoïcFaure-Lacroix так же, как reload(__builtins__) действителен в 2.x

JBernardo 20.06.2012 09:03

@Tarrasch: это модуль Python, который вы хотите перезагрузить, как в примере в вопросе.

Paul D. Waite 29.08.2013 13:17

@ LoïcFaure-Lacroix да, чертенок может перезагружаться.

Devyn Collier Johnson 24.11.2013 02:34

@ PaulD.Waite, можете подтвердить, что это работает в Python 3.6.5

aydow 07.06.2018 09:22

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

ahmettolga 12.10.2018 22:03

Для таких, как я, которые хотят выгрузить все модули (при работе в интерпретаторе Python под Emacs):

   for mod in sys.modules.values():
      reload(mod)

Более подробная информация находится в Перезагрузка модулей Python.

На самом деле, похоже, что это не работает надежно (в 2.6), потому что не все в sys.modules.values() является модулем. Например: >>> type (sys.modules.values ​​() [1]) <class 'email.LazyImporter'> Итак, если я попытаюсь запустить этот код, он упадет (я знаю, что это не практическое решение, просто указывая на это).

Francis Davey 18.06.2010 12:04

Это даже не работает с более ранними питонами - как написано. Пришлось исключить некоторые имена. Я обновлю сообщение, когда перенесу этот код на свой новый компьютер.

user3458 19.06.2010 09:32

Прекрасно работает в Python 2.7 после некоторых модификаций: if mod and mod.__name__ != "__main__": imp.reload(mod)

Czarek Tomczak 15.09.2012 18:24

У меня это хорошо работает: import imp [reload (m) for m in sys.modules.values ​​() if m, and not ""в м .__ имя and not imp.is_builtin (m .__ name__)]

Patrick Wolf 18.06.2015 02:40
if 'myModule' in sys.modules:  
    del sys.modules["myModule"]

+1. Моей целью было провести тесты на питоне. После того, как я загрузил модуль и переименовал некоторые функции, старые имена остались при вызове nose.run(), даже после reload(my_module)%run my_module

Peter D 11.09.2012 23:46

Если ваш модуль импортирует свои собственные подмодули, вам может потребоваться удалить и их. Что-то вроде [del(sys.modules[mod] for mod in sys.modules.keys() if mod.startswith('myModule.')].

drevicko 24.06.2014 13:00

Не думаю, что разгружает модуль. На Python 3.8: модуль import sys; import json; del sys.modules['json']; print(json.dumps([1])) и json все еще работает, хотя его больше нет в sys.modules.

Seperman 16.05.2020 09:37

да, я заметил подчеркнутую запись sys.modules ['_ json'], и она все еще распечатывается после принятия во внимание for mod in [ m for m in sys.modules if m.lstrip('_').startswith('json') ]: del sys.modules[mod]

Doyousketch2 15.10.2020 09:38

Ага. Даже при более агрессивном ведении бухгалтерского учета удаление записей вместе со всеми их зависимостями не лишает модуль возможности функционировать. before = [mod for mod in sys.modules] ; import json ; after = [mod for mod in sys.modules if mod not in before] ; for mod in [ m for m in sys.modules if m in after ]: del sys.modules[mod] (кодовый блок не сохранил символы новой строки; обозначает новую строку)

Doyousketch2 15.10.2020 09:43

Следующий код обеспечивает совместимость с Python 2/3:

try:
    reload
except NameError:
    # Python 3
    from imp import reload

Вы можете использовать его как reload() в обеих версиях, что упрощает работу.

Принятый ответ не обрабатывает случай from X import Y. Этот код обрабатывает это, а также стандартный случай импорта:

def importOrReload(module_name, *names):
    import sys

    if module_name in sys.modules:
        reload(sys.modules[module_name])
    else:
        __import__(module_name, fromlist=names)

    for name in names:
        globals()[name] = getattr(sys.modules[module_name], name)

# use instead of: from dfly_parser import parseMessages
importOrReload("dfly_parser", "parseMessages")

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

Заметили проблему, globals () относится к модулю, в котором вы определяете эту функцию, поэтому, если вы определяете его в модуле, отличном от того, который вы вызываете, это не сработает.

Joseph Garvin 12.10.2014 00:29

Для интерактивного, после >>> from X import Y для перезагрузки делать >>> __import__('X', fromlist='Y')

Bob Stein 29.01.2015 20:43

@ BobStein-VisiBone, есть ли способ заставить это работать, когда fromlist='*'?

Mike C 28.07.2015 22:55

Хороший вопрос, не знаю @MikeC. Между прочим, я стремлюсь полностью прекратить использование from в операторах импорта. Просто категоричный import <package> и явный package.symbol в коде. Поймите, что это не всегда возможно или желательно. (Вот одно исключение: из будущего импорта print_function.)

Bob Stein 29.07.2015 03:21

Майк С: мне подходит foo = reload(foo); from foo import *

rampion 19.10.2016 16:09

Добавьте import importlib и importlib.reload( ...

George Fisher 05.03.2017 21:37

Другой способ - импортировать модуль в функцию. Таким образом, когда функция завершает работу, модуль собирает мусор.

Модуль никогда не будет собирать мусор, потому что глобальная ссылка содержится, по крайней мере, в sys.modules.

All Workers Are Essential 21.10.2016 20:02

Для Python 2 используйте встроенную функцию reload:

reload(module)

Для Python 2а также Python 3,23.3 используйте reload из модуля imp:

import imp
imp.reload(module)

Для Python ≥3,4, impустарел в пользу importlib, поэтому используйте это:

import importlib
importlib.reload(module)

или же:

from importlib import reload
reload(module)

TL; DR:

Python ≥ 3.4: importlib.reload(module)
Python 3.2 - 3.3: imp.reload(module)
Python 2: reload(module)

для обработки любого из этих случаев: from six import reload_module (сначала, конечно, потребуется pip install six)

Anentropic 30.10.2017 19:46

@Anentropic: рекомендуется использовать шесть пакетов, но синтаксис - from six.moves import reload_module (док)

x0s 04.01.2018 23:32

В Enthought Traits есть модуль, который неплохо подходит для этого. https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html

Он перезагрузит любой модуль, который был изменен, и обновит другие модули и экземпляры объектов, которые его используют. Он не работает большую часть времени с методами __very_private__ и может подавиться наследованием классов, но он экономит мне сумасшедшее количество времени от необходимости перезапускать хост-приложение при написании Guis PyQt или тому подобном, что выполняется внутри программ, таких как Maya или Nuke. . Это не работает, может быть, в 20-30% случаев, но все же невероятно полезно.

Пакет Enthought не перезагружает файлы в тот момент, когда они меняются - вы должны называть это явно - но это не должно быть так сложно реализовать, если вам это действительно нужно.

для меня в случае с Abaqus так оно и работает. Представьте, что ваш файл - Class_VerticesEdges.py

sys.path.append('D:\...\My Pythons')
if 'Class_VerticesEdges' in sys.modules:  
    del sys.modules['Class_VerticesEdges']
    print 'old module Class_VerticesEdges deleted'
from Class_VerticesEdges import *
reload(sys.modules['Class_VerticesEdges'])

Этот ответ является прямой копией отсюда: ebanshi.cc/questions/1942/…

SiHa 12.10.2016 10:39

У меня было много проблем при попытке перезагрузить что-то внутри Sublime Text, но, наконец, я смог написать эту утилиту для перезагрузки модулей в Sublime Text на основе кода, который sublime_plugin.py использует для перезагрузки модулей.

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

def reload_module(full_module_name):
    """
        Assuming the folder `full_module_name` is a folder inside some
        folder on the python sys.path, for example, sys.path as `C:/`, and
        you are inside the folder `C:/Path With Spaces` on the file 
        `C:/Path With Spaces/main.py` and want to re-import some files on
        the folder `C:/Path With Spaces/tests`

        @param full_module_name   the relative full path to the module file
                                  you want to reload from a folder on the
                                  python `sys.path`
    """
    import imp
    import sys
    import importlib

    if full_module_name in sys.modules:
        module_object = sys.modules[full_module_name]
        module_object = imp.reload( module_object )

    else:
        importlib.import_module( full_module_name )

def run_tests():
    print( "\n\n" )
    reload_module( "Path With Spaces.tests.semantic_linefeed_unit_tests" )
    reload_module( "Path With Spaces.tests.semantic_linefeed_manual_tests" )

    from .tests import semantic_linefeed_unit_tests
    from .tests import semantic_linefeed_manual_tests

    semantic_linefeed_unit_tests.run_unit_tests()
    semantic_linefeed_manual_tests.run_manual_tests()

if __name__ == "__main__":
    run_tests()

Если вы запустите в первый раз, это должно загрузить модуль, но если позже вы снова можете использовать метод / функцию run_tests(), он перезагрузит файлы тестов. С Sublime Text (Python 3.3.6) это происходит часто, потому что его интерпретатор никогда не закрывается (если вы не перезапустите Sublime Text, то есть интерпретатор Python3.3).

Это современный способ перезагрузки модуля:

from importlib import reload

Если вы хотите поддерживать версии Python старше 3.5, попробуйте следующее:

from sys import version_info
if version_info[0] < 3:
    pass # Python 2 has built in reload
elif version_info[0] == 3 and version_info[1] <= 4:
    from imp import reload # Python 3.0 - 3.4 
else:
    from importlib import reload # Python 3.5+

Чтобы использовать его, запустите reload(MODULE), заменив MODULE на модуль, который вы хотите перезагрузить.

Например, reload(math) перезагрузит модуль math.

Или просто from importlib import reload. Тогда вы можете сделать reload(MODULE_NAME). В этой функции нет необходимости.

pault 02.02.2018 21:39

Я считаю, что modulereload(MODULE_NAME) более понятен, чем просто reload(MODULE_NAME), и имеет меньшую вероятность конфликта с другими функциями.

Richie Bendall 18.07.2018 01:30

@RichieBendall Извините, но это совершенно неверный ответ. Функция reload () принимает объект модуля, а не имя модуля ... Прочтите документацию: docs.python.org/3/library/importlib.html#importlib.reload И я согласен с @ pault - это "as modulereload" лишнее.

mbdevpl 01.08.2018 04:09

Я изменил свой ответ, чтобы отразить ваше мнение.

Richie Bendall 04.08.2018 07:34

2018-02-01

  1. модуль foo должен быть успешно импортирован заранее.
  2. from importlib import reload, reload(foo)

31.5. importlib - Реализация импорта - документация Python 3.6.4

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

Во-первых, убедитесь, что вы используете отличный Оболочка IPython из проекта Jupyter Notebook. После установки Jupyter вы можете запустить его с помощью ipython или jupyter console, или еще лучше, jupyter qtconsole, что даст вам красивую раскрашенную консоль с автозавершением кода в любой ОС.

Теперь в вашей оболочке введите:

%load_ext autoreload
%autoreload 2

Теперь, каждый раз, вы запускаете свой скрипт, ваши модули будут перезагружены.

Помимо 2, есть другие варианты магии автоперезагрузки:

%autoreload
Reload all modules (except those excluded by %aimport) automatically now.

%autoreload 0
Disable automatic reloading.

%autoreload 1
Reload all modules imported with %aimport every time before executing the Python code typed.

%autoreload 2
Reload all modules (except those excluded by %aimport) every time before
executing the Python code typed.

Конечно, он также будет работать на Jupyter Notebook.

Другой вариант. Обратите внимание, что importlib.reload Python по умолчанию просто повторно импортирует библиотеку, переданную в качестве аргумента. не будет перезагружает библиотеки, которые импортирует ваша библиотека. Если вы изменили много файлов и вам нужно импортировать несколько сложный пакет, вы должны выполнить глубокая перезагрузка.

Если у вас установлен IPython или Юпитер, вы можете использовать функцию для глубокой перезагрузки всех библиотек:

from IPython.lib.deepreload import reload as dreload
dreload(foo)

Если у вас нет Jupyter, установите его с помощью этой команды в своей оболочке:

pip3 install jupyter

И этот Ipython dreload, и reload () из importlib жалуются на reload() argument must be module. Я использую импорт настраиваемой функции и, похоже, не работает. Использование встроенных модулей действительно работает. :-( Это пустая трата времени перезагружать iPython для каждого небольшого изменения, которое я внес в свой код ...

m3nda 11.08.2018 16:58

Те, кто использует python 3 и перезагружаются из importlib.

Если у вас есть проблемы, например, кажется, что модуль не перезагружается ... Это связано с тем, что для перекомпиляции pyc требуется некоторое время (до 60 секунд). Я пишу эту подсказку только для того, чтобы вы знали, сталкивались ли вы с подобными проблемами.

Изменить (ответ V2)

Предыдущее решение хорошо только для получения информации о сбросе, но оно не изменит все ссылки (больше, чем reload, но меньше, чем требуется). Чтобы установить все ссылки, мне пришлось зайти в сборщик мусора и переписать там ссылки. Теперь это работает как шарм!

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

Новый код:

import importlib
import inspect
import gc
from weakref import ref


def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """

    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)

    # First, log all the references before reloading (because some references may be changed by the reload operation).
    module_tree = _get_tree_references_to_reset_recursively(module, module.__name__)

    new_module = importlib.reload(module)
    _reset_item_recursively(module, module_tree, new_module)


def _update_referrers(item, new_item):
    refs = gc.get_referrers(item)

    weak_ref_item = ref(item)
    for coll in refs:
        if type(coll) == dict:
            enumerator = coll.keys()
        elif type(coll) == list:
            enumerator = range(len(coll))
        else:
            continue

        for key in enumerator:

            if weak_ref_item() is None:
                # No refs are left in the GC
                return

            if coll[key] is weak_ref_item():
                coll[key] = new_item

def _get_tree_references_to_reset_recursively(item, module_name, grayed_out_item_ids = None):
    if grayed_out_item_ids is None:
        grayed_out_item_ids = set()

    item_tree = dict()
    attr_names = set(dir(item)) - _readonly_attrs
    for sub_item_name in attr_names:

        sub_item = getattr(item, sub_item_name)
        item_tree[sub_item_name] = [sub_item, None]

        try:
            # Will work for classes and functions defined in that module.
            mod_name = sub_item.__module__
        except AttributeError:
            mod_name = None

        # If this item was defined within this module, deep-reset
        if (mod_name is None) or (mod_name != module_name) or (id(sub_item) in grayed_out_item_ids) \
                or isinstance(sub_item, EnumMeta):
            continue

        grayed_out_item_ids.add(id(sub_item))
        item_tree[sub_item_name][1] = \
            _get_tree_references_to_reset_recursively(sub_item, module_name, grayed_out_item_ids)

    return item_tree


def _reset_item_recursively(item, item_subtree, new_item):

    # Set children first so we don't lose the current references.
    if item_subtree is not None:
        for sub_item_name, (sub_item, sub_item_tree) in item_subtree.items():

            try:
                new_sub_item = getattr(new_item, sub_item_name)
            except AttributeError:
                # The item doesn't exist in the reloaded module. Ignore.
                continue

            try:
                # Set the item
                _reset_item_recursively(sub_item, sub_item_tree, new_sub_item)
            except Exception as ex:
                pass

    _update_referrers(item, new_item)

Оригинальный ответ

Как написано в ответе @bobince, если уже есть ссылка на этот модуль в другом модуле (особенно если он был импортирован с ключевым словом as, например import numpy as np), этот экземпляр не будет перезаписан.

Это оказалось для меня довольно проблематичным при применении тестов, которые требовали «чистого» состояния модулей конфигурации, поэтому я написал функцию с именем reset_module, которая использует функцию importlibreload и рекурсивно перезаписывает все объявленные атрибуты модуля. Он был протестирован с Python версии 3.6.

import importlib
import inspect
from enum import EnumMeta

_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}


def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """

    new_module = importlib.reload(module)

    reset_items = set()

    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)

    _reset_item_recursively(module, new_module, module.__name__, reset_items)


def _reset_item_recursively(item, new_item, module_name, reset_items=None):
    if reset_items is None:
        reset_items = set()

    attr_names = set(dir(item)) - _readonly_attrs

    for sitem_name in attr_names:

        sitem = getattr(item, sitem_name)
        new_sitem = getattr(new_item, sitem_name)

        try:
            # Set the item
            setattr(item, sitem_name, new_sitem)

            try:
                # Will work for classes and functions defined in that module.
                mod_name = sitem.__module__
            except AttributeError:
                mod_name = None

            # If this item was defined within this module, deep-reset
            if (mod_name is None) or (mod_name != module_name) or (id(sitem) in reset_items) \
                    or isinstance(sitem, EnumMeta):  # Deal with enums
                continue

            reset_items.add(id(sitem))
            _reset_item_recursively(sitem, new_sitem, module_name, reset_items)
        except Exception as ex:
            raise Exception(sitem_name) from ex

Примечание: Используйте осторожно! Их использование в непериферийных модулях (например, в модулях, определяющих классы, используемые извне) может привести к внутренним проблемам в Python (например, проблемам травления / расправления).

Если вы столкнулись со следующей ошибкой, этот ответ может помочь вам найти решение:

Traceback (most recent call last): 
 File "FFFF", line 1, in  
NameError: name 'YYYY' is not defined

ИЛИ ЖЕ

Traceback (most recent call last):
 File "FFFF", line 1, in 
 File "/usr/local/lib/python3.7/importlib/__init__.py", line 140, in reload
   raise TypeError("reload() argument must be a module")
TypeError: reload() argument must be a module

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

  import importlib
  import sys

  from YYYY.XXX.ZZZ import CCCC
  import AAA.BBB.CC


  def reload(full_name)
    if full_name in sys.modules:
      importlib.reload(sys.modules[full_name])


  reload('YYYY.XXX.ZZZ') # this is fine in both cases
  reload('AAA.BBB.CC')  

  importlib.reload(YYYY.XXX.ZZZ) # in my case: this fails
  importlib.reload(AAA.BBB.CC)   #             and this is ok

Основная проблема в том, что importlib.reload принимает только модуль, а не строку.

Удаление модулей из sys.modules требует также удаления типов 'Никто'.

Способ 1:

import sys
import json  ##  your module

for mod in [ m for m in sys.modules if m.lstrip('_').startswith('json') or sys.modules[m] == None ]: del sys.modules[mod]

print( json.dumps( [1] ) )  ##  test if functionality has been removed

Метод 2 с использованием бухгалтерских записей для удаления зависимостей все:

import sys

before_import = [mod for mod in sys.modules]
import json  ##  your module
after_import = [mod for mod in sys.modules if mod not in before_import]

for mod in [m for m in sys.modules if m in after_import or sys.modules[m] == None]: del sys.modules[mod]

print( json.dumps( [2] ) )  ##  test if functionality has been removed

Необязательно, чтобы убедиться, что все записи отсутствуют, если вы так выберете:

import gc
gc.collect()

Другие вопросы по теме