Python: перезагрузить компонент Y, импортированный с помощью 'from X import Y'?


96

В Python, когда я импортировал модуль X в сеанс интерпретатора с использованием import X, и модуль изменился снаружи, я могу перезагрузить модуль с помощью reload(X). Затем изменения становятся доступны в моем сеансе интерпретатора.

Мне интересно, возможно ли это, когда я импортирую компонент Y из модуля X, используя from X import Y.

Этот оператор reload Yне работает, поскольку Y не является модулем, а только компонентом (в данном случае классом) внутри модуля.

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

РЕДАКТИРОВАТЬ:

Для пояснения вопрос касается импорта класса или функции Y из модуля X и перезагрузки при изменении, а не модуля Y из пакета X.


Я считаю, что в этом вопросе есть противоречие: ... possible ... import a component Y from module X«против question is ... importing a class or function X from a module Y». Я добавляю правку к этому эффекту.
Catskul

похоже, что отмеченный ответ на самом деле не отвечает на вопрос, я думаю, мой ответ отвечает. Можете обновить / прокомментировать?
Catskul

Ответы:


51

Если Y - модуль (а X - пакет), reload(Y)все будет в порядке - иначе вы поймете, почему в хороших руководствах по стилю Python (например, от моего работодателя) говорится, что никогда не следует импортировать ничего, кроме модуля (это одна из многих веских причин. - но люди до сих пор держать импортирования функции и классы напрямую, независимо от того , сколько я объясняю , что это не очень хорошая идея ;-).


1
Я понимаю вашу точку зрения. Не могли бы вы подробнее остановиться на каких-либо других веских причинах, почему это не очень хорошая идея?
cschol

6
@cschol: Zen of Python, последний стих ( import thisиз интерактивной подсказки, чтобы увидеть Zen of Python); и все причины, по которым пространства имен являются отличной идеей (немедленные локальные визуальные подсказки того, что имя просматривается, простота имитации / внедрения в тестах, возможность перезагрузки, возможность гибкого изменения модуля путем переопределения некоторых записей, предсказуемость и управляемость поведение при сериализации и восстановлении ваших данных [[например, путем травления и распаковки]] и так далее, и так далее - комментарий SO вряд ли достаточно длинный, чтобы отдать должное этому богатому, длинному аргументу !!! -)
Alex Martelli

4
обратите внимание, что в Python 3 перезагрузка больше не находится в пространстве имен по умолчанию, но была перемещена в importlibпакет. importlib.reload(Y) docs.python.org/3.4/library/… см. также stackoverflow.com/questions/961162/…
вылетает

4
@ThorSummoner, абсолютно нет - это означает «всегда импортировать МОДУЛИ», поэтому «from my.package import mymodule» абсолютно нормально и даже предпочтительнее - просто, никогда не импортируйте классы, функции и т.д. - всегда, только, всегда модули .
Алекс Мартелли

3
Голосовать против. Зачем? Это неправильный ответ. Правильный ответ дал Катскул 30 июля 2012 года в 15:04.
meh

105

Ответ

Судя по моим тестам, отмеченный ответ, предполагающий простой reload(X), не работает.

Из того, что я могу сказать, правильный ответ:

from importlib import reload # python 2.7 does not require this
import X
reload( X )
from X import Y

Контрольная работа

Мой тест был следующим (Python 2.6.5 + bpython 0.9.5.2)

X.py:

def Y():
    print "Test 1"

bpython:

>>> from X import Y
>>> print Y()
Test 1
>>> # Edit X.py to say "Test 2"
>>> print Y()
Test 1
>>> reload( X )  # doesn't work because X not imported yet
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'X' is not defined
>>> import X
>>> print Y()
Test 1
>>> print X.Y()
Test 1
>>> reload( X ) # No effect on previous "from" statements
>>> print Y()
Test 1
>>> print X.Y() # first one that indicates refresh
Test 2
>>> from X import Y
>>> print Y()
Test 2 
>>> # Finally get what we were after

1
Вау. Я нашел это действительно удобным. Благодарность! Сейчас я использую это как один лайнер: import X; перезагрузить (X); из X import Y
otterb

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

1
Кажется, это не всегда работает. У меня есть модуль, Fooкоторый __init__.pyвыбирает подмодуль ... Я отправлю ответ в качестве контрпримера.
Джейсон С.

Теперь один лайнер Python 3: import importlib; import X; importlib.reload (X); из X import Y
Уэйн

12
from modulename import func

import importlib, sys
importlib.reload(sys.modules['modulename'])
from modulename import func

Это лучший способ, я думаю, потому что вы, возможно, не помните, как именно он был импортирован
portforwardpodcast

Это единственное рабочее решение исходного вопроса, и у него слишком мало голосов!
CharlesB

1
в python 3 добавить: from importlib import reload
mirek

6

Во-первых, вам вообще не следует использовать перезагрузку, если вы можете этого избежать. Но предположим, что у вас есть причины (например, отладка внутри IDLE).

При перезагрузке библиотеки имена не вернутся в пространство имен модуля. Для этого достаточно переназначить переменные:

f = open('zoo.py', 'w')
f.write("snakes = ['viper','anaconda']\n")
f.close()

from zoo import snakes
print snakes

f = open('zoo.py', 'w')
f.write("snakes = ['black-adder','boa constrictor']\n")
f.close()

import zoo
reload(zoo)
snakes = zoo.snakes # the variable 'snakes' is now reloaded

print snakes

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


4

Если вы хотите это сделать:

from mymodule import myobject

Вместо этого сделайте это:

import mymodule
myobject=mymodule.myobject

Теперь вы можете использовать myobject так же, как и планировали (без утомительных нечитаемых ссылок на mymodule повсюду).

Если вы работаете в интерактивном режиме и хотите перезагрузить myobject из mymodule, теперь вы можете использовать:

reload(mymodule)
myobject=mymodule.myobject

2

предполагая, что вы использовали from X import Y, у вас есть два варианта:

reload(sys.modules['X'])
reload(sys.modules[__name__]) # or explicitly name your module

или

Y=reload(sys.modules['X']).Y

несколько соображений:

A. Если область импорта не распространяется на весь модуль (например, g: импорт в функции), вы должны использовать вторую версию.

B. если Y импортирован в X из другого модуля (Z) - вы должны перезагрузить Z, затем перезагрузить X, а затем перезагрузить модуль, даже перезагрузка всех ваших модулей (например, использование [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) может перезагрузить X перед перезагрузкой Z - и затем не обновлять значение Y.


1
  1. reload()модуль X,
  2. reload()импорт модуля Yиз X.

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


1

Если вы работаете в среде jupyter, и вы уже from module import functionможете использовать функцию магии, с autoreloadпомощью

%load_ext autoreload
%autoreload
from module import function

Введение autoreloadв IPython дается здесь .


0

Просто чтобы продолжить ответы AlexMartelli и Catskul , есть несколько действительно простых, но неприятных случаев, которые, кажется, сбивают с толку reload, по крайней мере, в Python 2.

Предположим, у меня есть следующее дерево исходников:

- foo
  - __init__.py
  - bar.py

со следующим содержанием:

init.py:

from bar import Bar, Quux

bar.py:

print "Loading bar"

class Bar(object):
  @property
  def x(self):
     return 42

class Quux(Bar):
  object_count = 0
  def __init__(self):
     self.count = self.object_count
     self.__class__.object_count += 1
  @property
  def x(self):
     return super(Quux,self).x + 1
  def __repr__(self):
     return 'Quux[%d, x=%d]' % (self.count, self.x)

Это прекрасно работает без использования reload:

>>> from foo import Quux
Loading bar
>>> Quux()
Quux[0, x=43]
>>> Quux()
Quux[1, x=43]
>>> Quux()
Quux[2, x=43]

Но попробуйте перезагрузить, и это либо не работает, либо портит вещи:

>>> import foo
Loading bar
>>> from foo import Quux
>>> Quux()
Quux[0, x=43]
>>> Quux()
Quux[1, x=43]
>>> reload(foo)
<module 'foo' from 'foo\__init__.pyc'>
>>> Quux()
Quux[2, x=43]
>>> from foo import Quux
>>> Quux()
Quux[3, x=43]
>>> reload(foo.bar)
Loading bar
<module 'foo.bar' from 'foo\bar.pyc'>
>>> Quux()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "foo\bar.py", line 17, in __repr__
    return 'Quux[%d, x=%d]' % (self.count, self.x)
  File "foo\bar.py", line 15, in x
    return super(Quux,self).x + 1
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> Quux().count
5
>>> Quux().count
6
>>> Quux = foo.bar.Quux
>>> Quux()
Quux[0, x=43]
>>> foo.Quux()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "foo\bar.py", line 17, in __repr__
    return 'Quux[%d, x=%d]' % (self.count, self.x)
  File "foo\bar.py", line 15, in x
    return super(Quux,self).x + 1
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> foo.Quux().count
8

Единственный способ обеспечить barперезагрузку подмодуля - это reload(foo.bar): единственный способ получить доступ к перезагруженному Quuxклассу - это достать его из перезагруженного подмодуля; но сам fooмодуль продолжал удерживать исходный Quuxобъект класса, предположительно потому, что он использует from bar import Bar, Quux(а не import barза ним Quux = bar.Quux); более того, Quuxкласс рассинхронизировался сам с собой, что просто странно.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.