Python: ricarica il componente Y importato con 'from X import Y'?


91

In Python, una volta che ho importato un modulo X in una sessione di interprete usando import X, e il modulo cambia all'esterno, posso ricaricare il modulo con reload(X). Le modifiche diventano quindi disponibili nella mia sessione di interprete.

Mi chiedo se ciò sia possibile anche quando importi un componente Y dal modulo X utilizzando from X import Y.

L'istruzione reload Ynon funziona, poiché Y non è un modulo in sé, ma solo un componente (in questo caso una classe) all'interno di un modulo.

È possibile ricaricare i singoli componenti di un modulo senza lasciare la sessione dell'interprete (o importare l'intero modulo)?

MODIFICARE:

Per chiarimenti, la domanda riguarda l'importazione di una classe o una funzione Y da un modulo X e il ricaricamento su una modifica, non un modulo Y da un pacchetto X.


Credo che ci sia una contraddizione in questa domanda: " ... possible ... import a component Y from module X" vs " question is ... importing a class or function X from a module Y". Sto aggiungendo una modifica a quell'effetto.
Catskul

sembra che la risposta contrassegnata non risponda effettivamente alla domanda, credo che la mia lo faccia. Puoi aggiornare / commentare?
Catskul

Risposte:


49

Se Y è un modulo (e X un pacchetto) reload(Y)andrà bene - altrimenti, vedrai perché le buone guide in stile Python (come quelle del mio datore di lavoro) dicono di non importare mai nulla tranne un modulo (questo è uno dei tanti ottimi motivi - eppure le persone continuano a importare funzioni e classi direttamente, non importa quanto io spieghi che non è una buona idea ;-).


1
Capisco il tuo punto. Ti andrebbe di approfondire uno degli altri buoni motivi per cui non è una buona idea?
cschol

6
@cschol: Zen of Python, ultimo verso ( import thisdal prompt interattivo per vedere lo Zen di Python); e tutti i motivi per cui gli spazi dei nomi sono una grande idea clamorosa (indizi visivi locali immediati che il nome viene cercato, facilità di derisione / iniezione nei test, capacità di ricaricare, capacità di un modulo di cambiare in modo flessibile ridefinendo alcune voci, prevedibile e controllabile comportamento durante la serializzazione e il ripristino dei dati [[ad es. decapaggio e deselezione]], e così via e così via - un commento SO non è abbastanza lungo per rendere giustizia a questo lungo e ricco argomento !!! -)
Alex Martelli

4
nota che in Python 3, reload non è più nello spazio dei nomi predefinito ma è stato spostato nel importlibpacchetto. importlib.reload(Y) docs.python.org/3.4/library/... vedi anche stackoverflow.com/questions/961162/...~~V~~plural~~3rd
mosche

4
@ThorSummoner, assolutamente no - significa "importa sempre MODULI", quindi "from my.package import mymodule" va assolutamente bene e anzi preferito - solo, non importare mai classi, funzioni, ecc - sempre, solo, sempre moduli .
Alex Martelli

2
Downvote. Perché? Questa non è la risposta giusta. La risposta giusta è stata data da Catskul il 30 luglio 12 alle 15:04.
meh

102

Risposta

Dai miei test, la risposta marcata, che suggerisce un semplice reload(X) , non funziona.

Da quello che posso dire la risposta corretta è:

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

Test

Il mio test è stato il seguente (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
Wow. L'ho trovato davvero utile. Grazie! Ora lo uso come un liner: import X; ricaricare (X); da X import Y
otterb

1
Questa è una risposta migliore di quella accettata. È giusto avvertire le persone, ma il caso d'uso di ognuno è diverso. In alcuni casi è davvero utile ricaricare una classe, ad esempio quando stai usando la console python e vuoi caricare le modifiche al tuo codice senza perdere la sessione.
nicb

Questo non sembra funzionare sempre. Ho un modulo Fooche ha un __init__.pyche recupera un sottomodulo ... posterò una risposta come controesempio.
Jason S

Python 3 one liner ora: import importlib; import X; importlib.reload (X); da X import Y
Wayne

12
from modulename import func

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

Questo è il modo migliore per imo perché potresti non ricordare esattamente come è stato importato
portforwardpodcast

Questa è l'unica soluzione funzionante alla domanda originale e ha troppi pochi voti!
CharlesB

1
in python 3 aggiungi: da importlib import reload
mirek

7

Prima di tutto, non dovresti usare affatto il reload, se puoi evitarlo. Ma supponiamo che tu abbia le tue ragioni (cioè il debug all'interno di IDLE).

Il ricaricamento della libreria non riporterà i nomi nello spazio dei nomi del modulo. Per fare ciò, riassegna semplicemente le variabili:

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

Puoi farlo in altri modi. Potresti automatizzare il processo cercando nello spazio dei nomi locale e riassegnando tutto ciò che era dal modulo in questione, ma penso che siamo già abbastanza malvagi.


4

Se vuoi farlo:

from mymodule import myobject

Fai questo invece:

import mymodule
myobject=mymodule.myobject

Ora puoi usare myobject nello stesso modo in cui stavi pianificando (senza i fastidiosi riferimenti illeggibili del miomodulo ovunque).

Se stai lavorando in modo interattivo e desideri ricaricare il mio oggetto dal miomodulo, ora puoi utilizzare:

reload(mymodule)
myobject=mymodule.myobject

2

supponendo che from X import Ytu abbia usato , hai due opzioni:

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

o

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

alcune considerazioni:

A. se l'ambito di importazione non è a livello di modulo (e, g: import in una funzione), è necessario utilizzare la seconda versione.

B. se Y è importato in X da un altro modulo (Z) - devi ricaricare Z, poi ricaricare X e poi ricaricare il tuo modulo, anche ricaricare tutti i tuoi moduli (e, g: usando [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) potrebbe ricaricare X prima di ricaricare Z - e poi non aggiorna il valore di Y.


1
  1. reload()modulo X,
  2. reload()modulo che importa Yda X.

Nota che il ricaricamento non cambierà gli oggetti già creati legati in altri spazi dei nomi (anche se segui la guida allo stile di Alex).


1

Se stai lavorando in un ambiente jupyter e hai già la from module import functionpossibilità di utilizzare la funzione magica, autoreloaddi

%load_ext autoreload
%autoreload
from module import function

L'introduzione di autoreloadin IPython è fornita qui .


0

Solo per dare seguito alle risposte di AlexMartelli e Catskul , ci sono alcuni casi davvero semplici ma sgradevoli che sembrano confonderereload , almeno in Python 2.

Supponiamo di avere il seguente albero dei sorgenti:

- foo
  - __init__.py
  - bar.py

con il seguente contenuto:

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)

Funziona perfettamente senza utilizzare reload:

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

Ma prova a ricaricare e non ha alcun effetto o corrompe le cose:

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

L'unico modo per assicurarmi che il barsottomodulo fosse ricaricato era quello di reload(foo.bar); l'unico modo per accedere alla Quuxclasse reloaded è raggiungerla e prenderla dal sottomodulo ricaricato; ma il foomodulo stesso continuava a mantenere l' Quuxoggetto della classe originale , presumibilmente perché utilizza from bar import Bar, Quux(anziché import barseguito da Quux = bar.Quux); inoltre la Quuxclasse non è più sincronizzata con se stessa, il che è semplicemente bizzarro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.