Python: przeładuj komponent Y zaimportowany z 'from X import Y'?

96

W Pythonie, po zaimportowaniu modułu X w sesji interpretera za pomocą import X, a moduł zmienia się na zewnątrz, mogę ponownie załadować moduł za pomocą reload(X). Zmiany będą następnie dostępne w mojej sesji tłumacza.

Zastanawiam się, czy jest to również możliwe, gdy importuję komponent Y z modułu X za pomocą from X import Y.

Instrukcja reload Ynie działa, ponieważ Y nie jest samym modułem, a jedynie składnikiem (w tym przypadku klasą) wewnątrz modułu.

Czy w ogóle możliwe jest przeładowanie poszczególnych elementów modułu bez wychodzenia z sesji tłumacza (lub importowania całego modułu)?

EDYTOWAĆ:

Dla wyjaśnienia, pytanie dotyczy importowania klasy lub funkcji Y z modułu X i ponownego wczytywania zmiany, a nie modułu Y z pakietu X.

cschol
źródło
Uważam, że w tym pytaniu jest sprzeczność: ... possible ... import a component Y from module X„kontra question is ... importing a class or function X from a module Y”. Dodaję zmianę w tym celu.
Catskul
wydaje się, że zaznaczona odpowiedź w rzeczywistości nie odpowiada na pytanie, a moim zdaniem tak. Czy możesz zaktualizować / skomentować?
Catskul,

Odpowiedzi:

51

Jeśli Y jest modułem (a X pakietem) reload(Y)będzie dobrze - w przeciwnym razie zobaczysz, dlaczego dobre przewodniki w stylu Pythona (takie jak mój pracodawca) mówią, aby nigdy nie importować niczego poza modułem (jest to jeden z wielu ważnych powodów - mimo to ludzie nadal bezpośrednio importują funkcje i klasy, nieważne jak bardzo wyjaśniam, że to nie jest dobry pomysł ;-).

Alex Martelli
źródło
1
Rozumiem co masz na myśli. Czy zechciałbyś rozwinąć inne dobre powody, dla których nie jest to dobry pomysł?
cschol
6
@cschol: Zen of Python, last verse ( import thisod interaktywnej zachęty, aby zobaczyć Zen of Python); i wszystkie powody, dla których przestrzenie nazw są świetnym pomysłem (natychmiastowe lokalne wizualne wskazówki, że nazwa jest wyszukiwana, łatwość mockowania / wstrzykiwania w testach, możliwość ponownego ładowania, możliwość elastycznej zmiany modułu poprzez przedefiniowanie niektórych wpisów, przewidywalna i kontrolowana zachowanie przy serializacji i odzyskiwaniu danych [[np. przez wytrawianie i wytrawianie]] i tak dalej, i tak dalej - komentarz SO nie jest wystarczająco długi, aby oddać sprawiedliwość temu bogatemu, długiemu argumentowi !!! -)
Alex Martelli
4
zwróć uwagę, że w Pythonie 3 funkcja reload nie znajduje się już w domyślnej przestrzeni nazw, ale została przeniesiona do importlibpakietu. importlib.reload(Y) docs.python.org/3.4/library/ ... zobacz także stackoverflow.com/questions/961162/ ...
leci
4
@ThorSummoner, absolutnie nie - to znaczy "zawsze importuj MODUŁY", więc "z my.package import mymodule" jest absolutnie w porządku i wręcz preferowane - po prostu nigdy nie importuj klas, funkcji itp. - zawsze, tylko, zawsze moduły .
Alex Martelli
3
Głosuj przeciw. Czemu? To nie jest dobra odpowiedź. Prawidłowej odpowiedzi udzielił Catskul 30 lipca 12 o 15:04.
meh
105

Odpowiedź

Z moich testów zaznaczona odpowiedź, która sugeruje prostą reload(X), nie działa.

Z tego, co mogę powiedzieć, poprawną odpowiedzią jest:

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

Test

Mój test był następujący (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
Catskul
źródło
1
Łał. Uważam, że to bardzo przydatne. Dzięki! Używam tego teraz jako jednej linijki: import X; przeładuj (X); z X import Y
otterb
1
To lepsza odpowiedź niż zaakceptowana. Ostrzeganie ludzi jest sprawiedliwe, ale każdy przypadek użycia jest inny. W niektórych przypadkach naprawdę pomocne jest ponowne załadowanie klasy, np. Gdy używasz konsoli Pythona i chcesz załadować zmiany w kodzie bez utraty sesji.
nicb
1
To nie zawsze działa. Mam moduł, Fooktóry ma moduł __init__.pypobierający podmoduł ... Napiszę odpowiedź jako kontrprzykład.
Jason S
Python 3 one liner teraz: import importlib; import X; importlib.reload (X); z X import Y
Wayne
12
from modulename import func

import importlib, sys
importlib.reload(sys.modules['modulename'])
from modulename import func
mirek
źródło
To najlepszy sposób na imo, ponieważ możesz nie pamiętać dokładnie, w jaki sposób został zaimportowany
portforwardpodcast,
To jedyne działające rozwiązanie pierwotnego pytania i ma za mało głosów!
CharlesB
1
w pythonie 3 dodaj: z importlib import reload
mirek
6

Po pierwsze, nie powinieneś w ogóle używać przeładowania, jeśli możesz tego uniknąć. Ale załóżmy, że masz swoje powody (np. Debugowanie wewnątrz IDLE).

Ponowne załadowanie biblioteki nie spowoduje przywrócenia nazw z powrotem do przestrzeni nazw modułu. Aby to zrobić, po prostu ponownie przypisz zmienne:

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

Możesz to zrobić na kilka innych sposobów. Możesz zautomatyzować ten proces, przeszukując lokalną przestrzeń nazw i ponownie przypisując wszystko, co pochodziło z danego modułu, ale myślę, że jesteśmy wystarczająco źli.

wisty
źródło
4

Jeśli chcesz to zrobić:

from mymodule import myobject

Zamiast tego zrób to:

import mymodule
myobject=mymodule.myobject

Możesz teraz używać myobject w taki sam sposób, w jaki planowałeś (bez męczących, nieczytelnych odniesień do mymodułu wszędzie).

Jeśli pracujesz interaktywnie i chcesz ponownie załadować myobject z mymodule, możesz teraz użyć:

reload(mymodule)
myobject=mymodule.myobject
Chris Fryer
źródło
2

zakładając, że korzystałeś from X import Y, masz dwie możliwości:

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

lub

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

kilka uwag:

A. jeśli zakres importu nie obejmuje całego modułu (np. Import w funkcji) - musisz użyć drugiej wersji.

B. jeśli Y jest importowany do X z innego modułu (Z) - musisz przeładować Z, a następnie przeładować X i przeładować moduł, nawet przeładowanie wszystkich modułów (np. Użycie [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) może przeładować X przed ponownym załadowaniem Z - a następnie nie odświeżać wartości Y.

Ohad Cohen
źródło
1
  1. reload()moduł X,
  2. reload()importowanie modułu Yz X.

Zauważ, że ponowne wczytanie nie zmieni już utworzonych obiektów powiązanych w innych przestrzeniach nazw (nawet jeśli zastosujesz się do przewodnika stylistycznego Alexa).

Denis Otkidach
źródło
1

Jeśli pracujesz w środowisku jupyter i już masz, from module import functionmożesz użyć magicznej funkcji, autoreloadwg

%load_ext autoreload
%autoreload
from module import function

Wprowadzenie do autoreloadIPythona jest podane tutaj .

Yanqi Huang
źródło
0

Aby podążać za odpowiedziami Alexa Martellego i Catskula , istnieje kilka naprawdę prostych, ale paskudnych przypadków, które wydają się być zagmatwane reload, przynajmniej w Pythonie 2.

Załóżmy, że mam następujące drzewo źródłowe:

- foo
  - __init__.py
  - bar.py

o następującej treści:

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)

Działa to dobrze bez użycia reload:

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

Ale spróbuj ponownie załadować, a to albo nie ma wpływu, albo psuje rzeczy:

>>> 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

Jedynym sposobem mogę zapewnić barsubmodule został załadowany było reload(foo.bar); jedynym sposobem, w jaki mogę uzyskać dostęp do przeładowanej Quuxklasy, jest sięgnięcie do niej i pobranie jej z ponownie załadowanego modułu podrzędnego; ale foosam moduł trzymał się oryginalnego Quuxobiektu klasy, prawdopodobnie dlatego, że używa from bar import Bar, Quux(a nie import barnastępuje po nim Quux = bar.Quux); ponadto Quuxklasa straciła synchronizację ze sobą, co jest po prostu dziwne.

Jason S.
źródło