Python: recharger le composant Y importé avec 'from X import Y'?

91

En Python, une fois que j'ai importé un module X dans une session d'interpréteur en utilisant import X, et que le module change à l'extérieur, je peux recharger le module avec reload(X). Les modifications deviennent alors disponibles dans ma session d'interprétation.

Je me demande si cela est également possible lorsque j'importe un composant Y du module X en utilisant from X import Y.

L'instruction reload Yne fonctionne pas, puisque Y n'est pas un module lui-même, mais seulement un composant (dans ce cas une classe) à l'intérieur d'un module.

Est-il possible du tout de recharger des composants individuels d'un module sans quitter la session d'interprétation (ou importer le module entier)?

ÉDITER:

Pour clarifier, la question concerne l'importation d'une classe ou d'une fonction Y depuis un module X et le rechargement sur un changement, pas un module Y depuis un package X.

cschol
la source
Je crois qu'il y a une contradiction dans cette question: " ... possible ... import a component Y from module X" vs " question is ... importing a class or function X from a module Y". J'ajoute une modification à cet effet.
Catskul
il semble que la réponse marquée ne répond pas réellement à la question, je crois que la mienne le fait. Pouvez-vous mettre à jour / commenter?
Catskul

Réponses:

49

Si Y est un module (et X un paquet) reload(Y)sera bien - sinon, vous verrez pourquoi les bons guides de style Python (comme mon employeur de) disent ne rien importer sauf un module (ce qui est l' une de plusieurs bonnes raisons - pourtant les gens continuent d'importer directement des fonctions et des classes, peu importe combien j'explique que ce n'est pas une bonne idée ;-).

Alex Martelli
la source
1
Je vois ce que tu veux dire. Souhaitez-vous élaborer sur l'une des autres bonnes raisons pour lesquelles ce n'est pas une bonne idée?
cschol
6
@cschol: Zen of Python, dernier couplet ( import thisdepuis l'invite interactive pour voir le Zen of Python); et toutes les raisons pour lesquelles les espaces de noms sont une excellente idée (indices visuels locaux immédiats que le nom est recherché, facilité de se moquer / injecter dans les tests, capacité de recharger, capacité pour un module de changer de manière flexible en redéfinissant certaines entrées, prévisible et contrôlable comportement sur la sérialisation et la récupération de vos données [[par exemple par décapage et décapage]], et ainsi de suite, et ainsi de suite - un commentaire SO est à peine assez long pour rendre justice à ce riche et long argument !!! -)
Alex Martelli
4
notez que dans Python 3, le rechargement n'est plus dans l'espace de noms par défaut mais a été déplacé vers le importlibpackage. importlib.reload(Y) docs.python.org/3.4/library/… voir aussi stackoverflow.com/questions/961162/...
vole
4
@ThorSummoner, absolument pas - cela signifie "toujours importer des MODULES", donc "de my.package import mymodule" est tout à fait correct et en effet préféré - il suffit de ne jamais importer de classes, de fonctions, etc. - toujours, seulement, jamais des modules .
Alex Martelli
2
Voter contre. Pourquoi? Ce n'est pas la bonne réponse. La bonne réponse a été donnée par Catskul le 30 juillet 2012 à 15h04.
meh
102

Répondre

D'après mes tests, la réponse marquée, qui suggère un simple reload(X), ne fonctionne pas.

D'après ce que je peux dire, la bonne réponse est:

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

Tester

Mon test était le suivant (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
la source
1
Sensationnel. J'ai trouvé cela très pratique. Merci! Je l'utilise maintenant comme une doublure: import X; recharger (X); de X import Y
otterb
1
C'est une meilleure réponse que celle acceptée. Il est juste d'avertir les gens, mais le cas d'utilisation de chacun est différent. Dans certains cas, il est vraiment utile de recharger une classe, par exemple lorsque vous utilisez la console python et que vous souhaitez charger des modifications dans votre code sans perdre votre session.
nicb
Cela ne semble pas toujours fonctionner. J'ai un module Fooqui a un __init__.pyqui récupère un sous-module ... Je posterai une réponse comme contre-exemple.
Jason S
Python 3 one liner maintenant: import importlib; import X; importlib.reload (X); de X import Y
Wayne
12
from modulename import func

import importlib, sys
importlib.reload(sys.modules['modulename'])
from modulename import func
Mirek
la source
C'est la meilleure façon imo car vous ne vous souvenez peut-être pas exactement comment il a été importé
portforwardpodcast
C'est la seule solution de travail à la question initiale, et a trop peu de votes!
CharlesB le
1
dans python 3 ajouter: depuis importlib import reload
mirek
7

Tout d'abord, vous ne devriez pas du tout utiliser le rechargement, si vous pouvez l'éviter. Mais supposons que vous ayez vos raisons (c'est-à-dire le débogage dans IDLE).

Le rechargement de la bibliothèque ne récupérera pas les noms dans l'espace de noms du module. Pour ce faire, réaffectez simplement les variables:

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

Vous pouvez le faire de plusieurs autres façons. Vous pouvez automatiser le processus en recherchant dans l'espace de noms local et en réaffectant tout ce qui provenait du module en question, mais je pense que nous sommes assez méchants.

mélancolique
la source
4

Si vous voulez faire ceci:

from mymodule import myobject

Faites ceci à la place:

import mymodule
myobject=mymodule.myobject

Vous pouvez maintenant utiliser myobject de la même manière que vous l'aviez prévu (sans les références mymodule illisibles fastidieuses partout).

Si vous travaillez de manière interactive et que vous souhaitez recharger myobject à partir de mymodule, vous pouvez désormais utiliser:

reload(mymodule)
myobject=mymodule.myobject
Chris Fryer
la source
2

en supposant que vous avez utilisé from X import Y, vous avez deux options:

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

ou

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

quelques considérations:

A. si la portée de l'importation n'est pas à l'échelle du module (e, g: importation dans une fonction) - vous devez utiliser la deuxième version.

B. si Y est importé dans X depuis un autre module (Z) - vous devez recharger Z, puis recharger X et que recharger votre module, même recharger tous vos modules (e, g: using [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) pourrait recharger X avant de recharger Z - et que pas rafraîchir la valeur de Y.

Ohad Cohen
la source
1
  1. reload()module X,
  2. reload()module importé Ydepuis X.

Notez que le rechargement ne changera pas les objets déjà créés liés dans d'autres espaces de noms (même si vous suivez le guide de style d'Alex).

Denis Otkidach
la source
1

Si vous travaillez dans un environnement Jupyter et que vous l'avez déjà fait, vous from module import functionpouvez utiliser la fonction magique, autoreloaden

%load_ext autoreload
%autoreload
from module import function

L'introduction du autoreloaddans IPython est donnée ici .

Yanqi Huang
la source
0

Juste pour faire suite aux réponses d'AlexMartelli et de Catskul , il y a des cas vraiment simples mais désagréables qui semblent confondre reload, du moins dans Python 2.

Supposons que j'ai l'arborescence des sources suivante:

- foo
  - __init__.py
  - bar.py

avec le contenu suivant:

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)

Cela fonctionne très bien sans utiliser reload:

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

Mais essayez de recharger et cela n'a aucun effet ou corrompt les choses:

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

La seule façon dont je pouvais m'assurer que le barsous - module était rechargé était de reload(foo.bar); la seule façon d'accéder à la Quuxclasse rechargée est de l'atteindre et de la récupérer dans le sous-module rechargé; mais le foomodule lui-même a continué à conserver l' Quuxobjet de classe d' origine , probablement parce qu'il utilise from bar import Bar, Quux(plutôt que import barsuivi par Quux = bar.Quux); de plus, la Quuxclasse s'est désynchronisée avec elle-même, ce qui est tout simplement bizarre.

Jason S
la source