2010-05-27 2 views
33

При разработке большого проекта (разбитого на несколько файлов и папок) на Python с помощью IPython я столкнулся с проблемой кэшированных импортированных модулей.Предотвращение кэширования импортированных модулей Python

Проблема заключается в том, что инструкции import module считывают модуль только один раз, даже если этот модуль изменился! Поэтому каждый раз, когда я что-то меняю в своем пакете, я должен выйти и перезапустить IPython. Болезненные.

Есть ли способ принудительно перезагрузить некоторые модули? Или, лучше, как-то помешать Python кэшировать их?

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

Единственный разумный ресурс, который я нашел, - Reloading Python modules, от pyunit, но я его не проверял. Мне хотелось бы что-то в этом роде.

Хорошей альтернативой было бы перезапустить IPython или как-либо перезапустить интерпретатор Python.

Итак, если вы разрабатываете в Python, какое решение вы нашли для этой проблемы?

Редактировать

Чтобы сделать вещи ясно: очевидно, я понимаю, что некоторые старые переменные в зависимости от предыдущего состояния модуля может остаться. Это прекрасно. Почему в Python так сложно заставить перезагрузить модуль без каких-либо странных ошибок?

Более конкретно, если у меня есть весь мой модуль в один файл module.py то следующие прекрасно работает:

import sys 
try: 
    del sys.modules['module'] 
except AttributeError: 
    pass 
import module 

obj = module.my_class() 

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

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

import os 
for mod in ['module.submod1', 'module.submod2']: 
    try: 
     del sys.module[mod] 
    except AttributeError: 
     pass 
# sometimes this works, sometimes not. WHY? 

Почему так различны для Python, есть ли у меня мой модуль в один большой файл или в нескольких подмодулей? Почему этот подход не работает?

+0

Это на самом деле довольно популярный вопрос; всплывают каждый месяц. Текущий консенсус заключается в том, чтобы перезапустить вашего переводчика, как отметил Майк. –

+2

Во-первых, я не понимаю, почему вы подавляете _AttributeError_. Просто нет способа 'del sys.modules [mod]' будет поднимать AttributeError ... кроме если вы отскакиваете 'sys' чем-то, кроме встроенного sys-модуля. Может быть просто, что во втором коде у вас есть 'sys.module' (который _will_ поднимает AttributeError с нормальным sys), а в первом вы имеете' sys.modules'? :-P – Veky

ответ

8

Преодоление и перезапуск переводчика - лучшее решение. Любая стратегия перепрограммирования в реальном времени или без кэширования не будет работать бесперебойно, поскольку объекты из более не существующих модулей могут существовать и потому, что модули иногда сохраняют состояние, и поскольку даже если ваш прецедент действительно разрешает горячую перезагрузку, это слишком сложно, чтобы думать о стоит того.

+4

Суеверие! Горячая перезагрузка достигается относительно элегантно даже в таких общих случаях, как [в PyUnit] (http://pyunit.sourceforge.net/notes/reloading.html), но в определенных случаях ее настройка не займет много времени и окупается очень Что ж. Например, в моей схеме есть легкий основной модуль, содержащий один сингл, который все мои другие экземпляры зависают, и это вызывает только их методы, а не функции. Перезагрузка выполняется с помощью 'для каждого i: del sys.modules [mymodule_i]', 'reload (..)' -ing, а затем заменяет '__class__' каждого экземпляра от старого до нового. –

+3

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

+0

За исключением, возможно, того, что целевую среду нельзя перезапустить так легко. Например, Blender требует перезапуска приложения, чтобы очистить состояние интерпретатора, что является ужасным пользовательским интерфейсом для надстроек, которые включаются и выключаются (дополнительный UI-интерфейс позволяет это), если надстройка больше, чем один модуль. – Coburn

21

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

Существует встроенная функция reload, которая, учитывая объект модуля, перезагрузит его с диска и будет размещена в sys.modules. Редактировать - на самом деле, он перекомпилирует код из файла на диске, а затем повторно выставит его в существующем модуле __dict__.Что-то потенциально очень отличается от создания нового объекта модуля.

Майк Грэм прав; получение перезагрузки прямо, если у вас есть еще несколько живых объектов, которые ссылаются на содержимое модуля, который вам больше не нужен, сложно. Существующие объекты по-прежнему будут ссылаться на классы, из которых они были созданы, является очевидной проблемой, но и все ссылки, созданные с помощью from module import symbol, будут по-прежнему указывать на любой объект из старой версии модуля. Возможно много тонко неправильных вещей.

Редактировать: Я согласен с консенсусом в отношении того, что перезапуск переводчика является, безусловно, самой надежной вещью. Но для целей отладки, я думаю, вы могли бы попробовать что-то вроде следующего. Я уверен, что есть угловые случаи, для которых это не сработает, но если вы не делаете ничего сумасшедшего (в противном случае) с загрузкой модуля в , ваш пакет, это может быть полезно.

def reload_package(root_module): 
    package_name = root_module.__name__ 

    # get a reference to each loaded module 
    loaded_package_modules = dict([ 
     (key, value) for key, value in sys.modules.items() 
     if key.startswith(package_name) and isinstance(value, types.ModuleType)]) 

    # delete references to these loaded modules from sys.modules 
    for key in loaded_package_modules: 
     del sys.modules[key] 

    # load each of the modules again; 
    # make old modules share state with new modules 
    for key in loaded_package_modules: 
     print 'loading %s' % key 
     newmodule = __import__(key) 
     oldmodule = loaded_package_modules[key] 
     oldmodule.__dict__.clear() 
     oldmodule.__dict__.update(newmodule.__dict__) 

который я очень кратко испытаны следующим образом:

import email, email.mime, email.mime.application 
reload_package(email) 

печать:

reloading email.iterators 
reloading email.mime 
reloading email.quoprimime 
reloading email.encoders 
reloading email.errors 
reloading email 
reloading email.charset 
reloading email.mime.application 
reloading email._parseaddr 
reloading email.utils 
reloading email.mime.base 
reloading email.message 
reloading email.mime.nonmultipart 
reloading email.base64mime 
+3

Я пробовал эти подходы, удаляя соответствующую запись в 'sys.modules' и используя' reload'. Он иногда работает, но иногда создает очень, очень тонкие и странные ошибки, где некоторые переменные внезапно становятся «Нет» без всякой причины. –

+3

«Доктор, мне больно, когда я так люблю», «Ну, не делай этого». Я всегда рассматривал 'reload' как просто удобство при отладке и считал, что это ошибка документации, которую она предписывает для реальной живой загрузки, учитывая, насколько плохими могут быть семантические ошибки, которые она предоставляет. – msw

+0

Я нашел строку 'del sys.modules [key]' вашего ответа полезной для моего сценария. Я согласен, что это не мешает кешированию в качестве заданного вопроса, но эти функции могут помочь создать такое поведение, как периодическое истечение кеша python. –

2

Вы можете использовать импорт крюк машины, описанный в PEP 302 для нагрузки не модули самих устройств, но какой-то прокси объект, который позволит вам делать все, что хотите, с базовым объектом модуля - перезагрузить его, отбросить ссылку на него и т. д.

Дополнительным преимуществом является то, что существующий в настоящее время код не требует изменений, и эту дополнительную функциональность модуля можно оторвать от одной точки в коде - там, где вы действительно добавляете искатель в sys.meta_path.

Некоторые мысли о реализации: создайте поиск, который согласится найти любой модуль, кроме встроенного (вы не имеете ничего общего со встроенными модулями), а затем создайте загрузчик, который будет возвращать объект-прокси подкласса от types.ModuleType вместо реального объекта модуля. Обратите внимание, что объект-загрузчик не вынужден создавать явные ссылки на загруженные модули в sys.modules, но настоятельно рекомендуется, потому что, как вы уже видели, он может неожиданно завершиться. Объект прокси должен перехватывать и переадресовывать все __getattr__, __setattr__ и __delattr__ в базовый реальный модуль, на который ссылается. Вам, вероятно, не нужно будет определять __getattribute__ из-за того, что вы не скрывали бы содержимое реального модуля с помощью ваших прокси-методов. Итак, теперь вы должны каким-то образом взаимодействовать с прокси-сервером - вы можете создать специальный метод для удаления базовой ссылки, затем импортировать модуль, извлечь ссылку из возвращенного прокси-сервера, сбросить прокси-сервер и сохранить ссылку на перезагруженный модуль. Фу, выглядит страшно, но нужно исправить вашу проблему, не перезагружая Python каждый раз.

7

С IPython приходит autoreload extension, который автоматически повторяет импорт перед каждым вызовом функции. Он работает, по крайней мере, в простых случаях, но не слишком полагается на него: по моему опыту, время от времени требуется перезапуск интерпретатора, особенно когда изменения кода происходят только на косвенно импортированном коде.

Пример использования от связанной страницы:

In [1]: %load_ext autoreload 

In [2]: %autoreload 2 

In [3]: from foo import some_function 

In [4]: some_function() 
Out[4]: 42 

In [5]: # open foo.py in an editor and change some_function to return 43 

In [6]: some_function() 
Out[6]: 43 
+1

Я обнаружил, что, хотя '% autoreload 2' не работает, когда есть глубокие иерархии, это решение всегда работает: http: //stackoverflow.com/a/13096672/311567, который, я надеюсь, станет стандартным по умолчанию в ipython – dashesy

3

Есть некоторые действительно хорошие ответы уже здесь, но стоит знать о dreload, которая является функцией, доступной в IPython, которая делает как «глубокой перезагрузки». Из документации:

IPython.lib.Модуль deepreload позволяет рекурсивно перезагружать модуль : изменения, внесенные в любую из его зависимостей, будут перезагружены без необходимости выхода. Для того, чтобы начать использовать его, сделайте следующее:

http://ipython.org/ipython-doc/dev/interactive/reference.html#dreload

Он доступен как «глобальный» в IPython ноутбука (по крайней мере моей версии, которая работает v2.0).

HTH

2

Я использую PythonNet в своем проекте. К счастью, я обнаружил, что есть команда, которая может прекрасно решить эту проблему.

using (Py.GIL()) 
     { 
      dynamic mod = Py.Import(this.moduleName); 
      if (mod == null) 
       throw new Exception(string.Format("Cannot find module {0}. Python script may not be complied successfully or module name is illegal.", this.moduleName)); 

      // This command works perfect for me! 
      PythonEngine.ReloadModule(mod); 

      dynamic instance = mod.ClassName(); 
+1

. Я считаю, что вопрос заключается в перезапуске интерпретатора python. Кажется, вы используете в нем C# и python. ? Proabaly не то, что ищет OP. – Vasif

+0

@ user7175781 Синтаксис не работает над моей системой, независимо от того, что я пытаюсь. Я думаю, что PythonNet отличается от синтаксиса и API, чем ipython. Я надеялся заставить ваш пример работать, но после многих ошибок, о которых сообщает интерпретатор, я наконец понял, что вы сказали язык PythonNet. –

Смежные вопросы