Python: ¿recargar el componente Y importado con 'from X import Y'?


91

En Python, una vez que he importado un módulo X en una sesión de intérprete usando import X, y el módulo cambia en el exterior, puedo recargar el módulo con reload(X). Luego, los cambios estarán disponibles en mi sesión de intérprete.

Me pregunto si esto también es posible cuando importo un componente Y desde el módulo X usando from X import Y.

La declaración reload Yno funciona, ya que Y no es un módulo en sí mismo, sino solo un componente (en este caso una clase) dentro de un módulo.

¿Es posible volver a cargar componentes individuales de un módulo sin salir de la sesión de intérprete (o importar el módulo completo)?

EDITAR:

Para aclarar, la pregunta es sobre importar una clase o función Y desde un módulo X y recargar en un cambio, no un módulo Y de un paquete X.


Creo que hay una contradicción en esta pregunta: " ... possible ... import a component Y from module X" vs " question is ... importing a class or function X from a module Y". Estoy agregando una edición a ese efecto.
Catskul

parece que la respuesta marcada no responde realmente a la pregunta, creo que la mía sí. ¿Puedes actualizar / comentar?
Catskul

Respuestas:


49

Si Y es un módulo (y X un paquete) reload(Y)estará bien; de lo contrario, verá por qué las buenas guías de estilo de Python (como las de mi empleador) dicen que nunca importe nada excepto un módulo (esta es una de las muchas razones importantes - sin embargo, la gente todavía mantienen la importación de funciones y clases directamente, sin importar lo mucho que explicar que es no una idea buena ;-).


1
Entiendo tu argumento. ¿Le importaría desarrollar alguna de las otras buenas razones por las que no es una buena idea?
cschol

6
@cschol: Zen de Python, último verso ( import thisdel indicador interactivo para ver el Zen de Python); y todas las razones por las que los espacios de nombres son una gran idea (pistas visuales locales inmediatas de que se está buscando el nombre, facilidad para burlarse / inyectarse en las pruebas, capacidad de recarga, capacidad de un módulo para cambiar de manera flexible al redefinir algunas entradas, predecibles y controlables comportamiento en la serialización y recuperación de sus datos [[p. ej., decapando y despegando]], y así sucesivamente, ¡¡¡un comentario SO apenas es lo suficientemente largo para hacer justicia a este rico y largo argumento !!! -)
Alex Martelli

4
tenga en cuenta que en Python 3, la recarga ya no está en el espacio de nombres predeterminado, pero se ha movido al importlibpaquete. importlib.reload(Y) docs.python.org/3.4/library/… ver también stackoverflow.com/questions/961162/…
vuela el

4
@ThorSummoner, absolutamente no - significa "siempre importar MÓDULOS", así que "desde my.package importar mymodule" está absolutamente bien y de hecho es preferido - solo, nunca importar clases, funciones, etc. - siempre, solo, nunca módulos .
Alex Martelli

2
Votar en contra. ¿Por qué? Ésta no es la respuesta correcta. Catskul dio la respuesta correcta el 30 de julio de 2012 a las 15:04.
meh

102

Responder

De mis pruebas, la respuesta marcada, que sugiere una simple reload(X), no funciona.

Por lo que puedo decir, la respuesta correcta es:

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

Prueba

Mi prueba fue la siguiente (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
Guau. Encontré esto realmente útil. ¡Gracias! Ahora uso esto como un trazador de líneas: importar X; recargar (X); de X import Y
otterb

1
Esta es una mejor respuesta que la aceptada. Es justo advertir a la gente, pero el caso de uso de todos es diferente. En algunos casos, es realmente útil volver a cargar una clase, por ejemplo, cuando está usando la consola de Python y desea cargar cambios en su código sin perder su sesión.
nicb

Esto no parece funcionar siempre. Tengo un módulo Fooque tiene un __init__.pyque busca un submódulo ... Publicaré una respuesta como contraejemplo.
Jason S

Python 3 one liner ahora: importar importlib; importar X; importlib.reload (X); de X importar Y
Wayne

12
from modulename import func

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

Esta es la mejor manera, en mi opinión, porque es posible que no recuerde exactamente cómo se importó
portforwardpodcast

Esa es la única solución funcional a la pregunta original, ¡y tiene muy pocos votos!
CharlesB

1
en python 3 agregue: desde importlib import reload
mirek

7

En primer lugar, no debería utilizar la recarga en absoluto, si puede evitarlo. Pero supongamos que tiene sus razones (es decir, depurar dentro de IDLE).

Recargar la biblioteca no devolverá los nombres al espacio de nombres del módulo. Para hacer esto, simplemente reasigne las 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

Puede hacer esto de otras formas. Puede automatizar el proceso buscando en el espacio de nombres local y reasignando todo lo que fuera del módulo en cuestión, pero creo que estamos siendo lo suficientemente malvados.


4

Si quieres hacer esto:

from mymodule import myobject

Haz esto en su lugar:

import mymodule
myobject=mymodule.myobject

Ahora puede usar myobject de la misma manera que estaba planeando (sin las tediosas referencias ilegibles de mymodule en todas partes).

Si está trabajando de forma interactiva y desea volver a cargar myobject desde mymodule, ahora puede usar:

reload(mymodule)
myobject=mymodule.myobject

2

suponiendo que lo haya usado from X import Y, tiene dos opciones:

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

o

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

algunas consideraciones:

A. si el alcance de la importación no abarca todo el módulo (p. Ej., Importar en una función), debe utilizar la segunda versión.

B. si Y se importa a X desde otro módulo (Z), debe recargar Z, luego recargar X y luego recargar su módulo, incluso recargar todos sus módulos (p. Ej., Usar [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) podría recargar X antes de recargar Z, y luego no refrescar el valor de Y.


1
  1. reload()módulo X,
  2. reload()módulo de importación Yde X.

Tenga en cuenta que la recarga no cambiará los objetos ya creados enlazados en otros espacios de nombres (incluso si sigue la guía de estilo de Alex).


1

Si está trabajando en un entorno jupyter, y ya tiene from module import functionpuede utilizar la función de magia, autoreloadpor

%load_ext autoreload
%autoreload
from module import function

La introducción de autoreloaden IPython se da aquí .


0

Solo para dar seguimiento a las respuestas de AlexMartelli y Catskul , hay algunos casos realmente simples pero desagradables que parecen confundir reload, al menos en Python 2.

Supongamos que tengo el siguiente árbol de fuentes:

- foo
  - __init__.py
  - bar.py

con el siguiente contenido:

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)

Esto funciona bien sin usar reload:

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

Pero intente recargar y no tiene ningún efecto o corrompe las cosas:

>>> 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 única forma en que podía asegurarme de que barse volviera a cargar el submódulo era reload(foo.bar); la única forma en que accedo a la Quuxclase recargada es acercarme y tomarla del submódulo recargado; pero el foomódulo en sí se mantuvo aferrado al Quuxobjeto de clase original , presumiblemente porque usa from bar import Bar, Quux(en lugar de import barseguido de Quux = bar.Quux); además, la Quuxclase se desincronizó consigo misma, lo cual es simplemente extraño.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.