Python: Komponente Y neu laden, importiert mit 'von X importieren Y'?


91

In Python kann ich import Xdas Modul mit neu laden , nachdem ich ein Modul X in einer Interpretersitzung mit importiert habe und das Modul außen geändert hat reload(X). Die Änderungen werden dann in meiner Dolmetschersitzung verfügbar.

Ich frage mich, ob dies auch möglich ist, wenn ich eine Komponente Y aus Modul X mit importiere from X import Y.

Die Anweisung reload Yfunktioniert nicht, da Y kein Modul selbst ist, sondern nur eine Komponente (in diesem Fall eine Klasse) innerhalb eines Moduls.

Ist es überhaupt möglich, einzelne Komponenten eines Moduls neu zu laden, ohne die Interpretersitzung zu verlassen (oder das gesamte Modul zu importieren)?

BEARBEITEN:

Zur Verdeutlichung geht es darum, eine Klasse oder Funktion Y aus einem Modul X zu importieren und bei einer Änderung neu zu laden, nicht ein Modul Y aus einem Paket X.


Ich glaube, dass diese Frage einen Widerspruch enthält: " ... possible ... import a component Y from module X" vs " question is ... importing a class or function X from a module Y". Ich füge eine Bearbeitung zu diesem Effekt hinzu.
Catskul

es scheint, dass die markierte Antwort die Frage nicht wirklich beantwortet, ich glaube meine. Können Sie aktualisieren / kommentieren?
Catskul

Antworten:


48

Wenn Y ein Modul (und X ein Paket) ist reload(Y)wird in Ordnung sein - sonst werden Sie sehen, warum Python gute Styleguides (wie mein Arbeitgeber) sagen nie Import nichts außer einem Modul (dieses ist von vielen großen Gründe aus - Trotzdem importieren die Leute weiterhin Funktionen und Klassen direkt, egal wie sehr ich erkläre, dass es keine gute Idee ist ;-).


1
Ich weiß, worauf du hinauswillst. Möchten Sie einen der anderen guten Gründe erläutern, warum dies keine gute Idee ist?
Cschol

6
@cschol: Zen of Python, letzter Vers ( import thisvon der interaktiven Eingabeaufforderung, um das Zen of Python zu sehen); und all die Gründe, warum Namespaces eine großartige Idee sind (unmittelbare lokale visuelle Hinweise darauf, dass der Name nachgeschlagen wird, einfache Verspottung / Injektion in Tests, Fähigkeit zum Neuladen, Fähigkeit eines Moduls, sich durch Neudefinition einiger Einträge flexibel zu ändern, vorhersehbar und kontrollierbar Verhalten bei der Serialisierung und Wiederherstellung Ihrer Daten [[z. B. durch Beizen und Entpicken]] usw. - ein SO-Kommentar ist kaum lang genug, um diesem reichen, langen Argument gerecht zu werden !!! -)
Alex Martelli

4
Beachten Sie, dass sich das Neuladen in Python 3 nicht mehr im Standard-Namespace befindet, sondern in das importlibPaket verschoben wurde . importlib.reload(Y) docs.python.org/3.4/library/… siehe auch stackoverflow.com/questions/961162/…
fliegt

4
@ThorSummoner, absolut nicht - es bedeutet "immer MODULE importieren", also ist "aus my.package import mymodule" absolut in Ordnung und in der Tat bevorzugt - nur niemals Klassen, Funktionen usw. importieren - immer, nur, immer Module .
Alex Martelli

2
Downvote. Warum? Dies ist nicht die richtige Antwort. Die richtige Antwort wurde von Catskul am 30. Juli 12 um 15:04 gegeben.
meh

102

Antworten

Nach meinen Tests reload(X)funktioniert die markierte Antwort, die eine einfache andeutet , nicht.

Nach allem, was ich sagen kann, lautet die richtige Antwort:

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

Prüfung

Mein Test war der folgende (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
Beeindruckend. Ich fand das sehr praktisch. Vielen Dank! Ich benutze dies jetzt als einen Liner: import X; neu laden (X); von X import Y
otterb

1
Dies ist eine bessere Antwort als die akzeptierte. Es ist fair, Leute zu warnen, aber jeder Anwendungsfall ist anders. In einigen Fällen ist es wirklich hilfreich, eine Klasse neu zu laden, z. B. wenn Sie die Python-Konsole verwenden und Änderungen an Ihrem Code laden möchten, ohne Ihre Sitzung zu verlieren.
Nicb

Das scheint nicht immer zu funktionieren. Ich habe ein Modul, Foodas __init__.pyein Submodul abruft ... Ich werde eine Antwort als Gegenbeispiel posten.
Jason S

Python 3 jetzt ein Liner: import importlib importieren; X importieren; importlib.reload (X); von X Import Y
Wayne

11
from modulename import func

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

Dies ist der beste Weg imo, weil Sie sich möglicherweise nicht genau erinnern, wie es importiert wurde
portforwardpodcast

Das ist die einzige funktionierende Lösung für die ursprüngliche Frage und hat zu wenig Stimmen!
CharlesB

1
in Python 3 hinzufügen: von importlib import
reload

7

Zunächst einmal sollten Sie Reload überhaupt nicht verwenden, wenn Sie dies vermeiden können. Nehmen wir jedoch an, Sie haben Ihre Gründe (dh das Debuggen in IDLE).

Beim erneuten Laden der Bibliothek werden die Namen nicht wieder in den Namespace des Moduls übernommen. Weisen Sie dazu einfach die Variablen neu zu:

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

Sie können dies auf einige andere Arten tun. Sie könnten den Prozess automatisieren, indem Sie den lokalen Namespace durchsuchen und alles neu zuweisen, was aus dem betreffenden Modul stammt, aber ich denke, wir sind böse genug.


4

Wenn Sie dies tun möchten:

from mymodule import myobject

Tun Sie dies stattdessen:

import mymodule
myobject=mymodule.myobject

Sie können myobject jetzt genauso verwenden, wie Sie es geplant haben (ohne die lästigen unlesbaren mymodule-Referenzen überall).

Wenn Sie interaktiv arbeiten und myobject von mymodule neu laden möchten, können Sie jetzt Folgendes verwenden:

reload(mymodule)
myobject=mymodule.myobject

2

Angenommen from X import Y, Sie haben verwendet , haben Sie zwei Möglichkeiten:

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

oder

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

Einige Überlegungen:

A. Wenn der Importbereich nicht modulweit ist (z. B. Import in eine Funktion), müssen Sie die zweite Version verwenden.

B. Wenn Y von einem anderen Modul (Z) in X importiert wird - Sie müssen Z neu laden, dann X neu laden und dann Ihr Modul neu laden. Selbst das Neuladen aller Module (z. B. using [ reload(mod) for mod in sys.modules.values() if type(mod) == type(sys) ]) kann X vor dem erneuten Laden von Z neu laden - und dann den Wert von Y nicht aktualisieren.


1
  1. reload()Modul X,
  2. reload()Modulimport Yaus X.

Beachten Sie, dass durch das erneute Laden bereits erstellte Objekte, die in anderen Namespaces gebunden sind, nicht geändert werden (auch wenn Sie dem Styleguide von Alex folgen).


1

Wenn Sie in einer jupyter Umgebung arbeiten, und haben Sie bereits from module import functiondie magische Funktion verwenden, autoreloaddurch

%load_ext autoreload
%autoreload
from module import function

Die Einführung autoreloadin IPython finden Sie hier .


0

Um den Antworten von AlexMartelli und Catskul nachzugehen , gibt es einige wirklich einfache, aber böse Fälle, die verwirrend zu sein scheinenreload zumindest in Python 2 .

Angenommen, ich habe den folgenden Quellbaum:

- foo
  - __init__.py
  - bar.py

mit folgendem Inhalt:

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)

Dies funktioniert einwandfrei ohne reload:

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

Aber versuchen Sie neu zu laden und es hat entweder keine Wirkung oder korrumpiert Dinge:

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

Der einzige Weg, wie ich sicherstellen konnte, dass das barSubmodul neu geladen wurde, war reload(foo.bar): Die einzige Möglichkeit, auf die neu geladene QuuxKlasse zuzugreifen, besteht darin, sie vom neu geladenen Untermodul zu erreichen und zu greifen. aber das fooModul selbst hielt am ursprünglichen QuuxKlassenobjekt fest, vermutlich weil es verwendet from bar import Bar, Quux(anstatt import bargefolgt von Quux = bar.Quux); Außerdem war die QuuxKlasse nicht mehr mit sich selbst synchronisiert, was nur bizarr ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.