Python: перезавантажити компонент Y, імпортований за допомогою 'from X import Y'?


91

У 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

Відповіді:


49

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


1
Я бачу вашу думку. Чи не хотіли б Ви детальніше розказати про інші вагомі причини, чому це не є гарною ідеєю?
cschol

6
@cschol: дзен Python, останній вірш ( import thisз інтерактивного запиту, щоб побачити дзен Python); і всі причини, чому простори імен є чудовою чудовою ідеєю (безпосередні локальні візуальні підказки про те, що ім'я шукається, легкість знущань / ін'єкцій у тестах, можливість перезавантаження, можливість модуля гнучко змінюватись шляхом перевизначення деяких записів, передбачувана та керована поведінка щодо серіалізації та відновлення ваших даних [[наприклад, за допомогою маринування та видалення]], і так далі, і так далі - ТАКИЙ коментар навряд чи достатньо довгий, щоб виправдати цей багатий, довгий аргумент !!! -)
Алекс Martelli

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

4
@ThorSummoner, абсолютно ні - це означає "завжди імпортувати МОДУЛІ", тому "з my.package import mymodule" абсолютно нормально і дійсно бажано - просто, ніколи не імпортувати класи, функції тощо - завжди, тільки, коли-небудь модулі .
Алекс Мартеллі,

2
Проголосувати проти. Чому? Це не правильна відповідь. Правильну відповідь дав Кацкул 30 липня '12 о 15:04.
meh

102

Відповідь

З моїх тестів позначена відповідь, яка передбачає просту 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 імпорт Y
otterb

1
Це краща відповідь, ніж прийнята. Чесно попередити людей, але у всіх випадки використання різні. У деяких випадках корисно перезавантажити клас, наприклад, коли ви використовуєте консоль python і хочете завантажити зміни до коду, не втрачаючи сеансу.
nicb

Здається, це не завжди працює. У мене є модуль, Fooякий має __init__.pyфайл, який отримує підмодуль ... Я розміщу відповідь як контрприклад.
Jason S,

Зараз один вкладиш Python 3: import importlib; import X; importlib.reload (X); від X import Y
Wayne

12
from modulename import func

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

Це найкращий спосіб imo, тому що ви, можливо, не пам’ятаєте, як саме він був імпортований
portforwardpodcast

Це єдине робоче рішення вихідного питання, і у нього занадто мало голосів!
CharlesB

1
в python 3 додати: з importlib import reload
mirek

7

По-перше, вам не слід використовувати перезавантаження взагалі, якщо ви можете цього уникнути. Але припустимо, у вас є свої причини (тобто налагодження всередині 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

кілька міркувань:

А. якщо область імпорту не є загальномодульною (e, g: import у функції) - ви повинні використовувати другу версію.

B. якщо Y імпортується в X з іншого модуля (Z) - ви повинні перезавантажити Z, тоді перезавантажте X і перезавантажте свій модуль, навіть перезавантаживши всі ваші модулі (e, g: using [ 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

Щоб продовжити відповіді Алекса Мартеллі та Кацкула , є кілька справді простих, але неприємних випадків, які, здається, бентежать 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.