Kreisförmige (oder zyklisch) Einfuhren in Python
-
09-09-2019 - |
Frage
Was passiert, wenn zwei Module sie importieren?
Um das Problem zu verallgemeinern, was über die zyklischen Einfuhren in Python?
Lösung
Es war eine wirklich gute Diskussion zu diesem über comp. lang.python im letzten Jahr. Es beantwortet Ihre Frage ziemlich gründlich.
Die Importe sind wirklich ziemlich einfach. Denken Sie daran, wie folgt vor:
'Import' und 'von xxx Import yyy' ausführbare Anweisungen sind. sie führen wenn das laufende Programm erreicht diese Zeile.
Wenn ein Modul nicht in sys.modules ist, dann erzeugt ein Import das neue Modul Eintrag in sys.modules und führt dann den Code in dem Modul. Es tut nicht Rück Steuerung an das anrufende Modul, bis die Ausführung abgeschlossen hat.
Wenn ein Modul in sys.modules existiert dann ein Import einfach zurückgibt, dass Modul, ob es die Ausführung abgeschlossen ist. Das ist der Grund, warum zyklische Importe Module zurückkehren können, die teilweise leer erscheinen.
Schließlich läuft das Ausführen von Skripts in einem Modul namens __main__, Importieren das Skript unter eigenem Namen ein neues Modul in keinem Zusammenhang erstellen __main __.
Nehmen Sie die Menge zusammen und sollten Sie keine Überraschungen, beim Import Module.
Andere Tipps
Wenn Sie in import foo
und bar
innen import bar
foo
tun, wird es gut funktionieren. Durch die Zeit, alles tatsächlich läuft, werden beide Module geladen wurde und Verweise miteinander haben werden.
Das Problem ist, wenn Sie stattdessen from foo import abc
und from bar import xyz
tun. Da nun jedes Modul erfordert das andere Modul bereits importiert werden (so dass der Name, den wir importieren vorhanden), bevor es importiert werden kann.
Zyklische Importe beenden, aber Sie müssen vorsichtig sein, nicht die konjunktur importierten Module während Modulinitialisierung zu verwenden.
Beachten Sie die folgenden Dateien:
a.py:
print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"
b.py:
print "b in"
import a
print "b out"
x = 3
Wenn Sie a.py ausführen, Sie erhalten die folgende:
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out
Auf dem zweiten Import von b.py (in der zweiten a in
), das Python-Interpreter nicht importiert b
wieder, weil es bereits im Modul dict vorhanden ist.
Wenn Sie versuchen, b.x
zuzugreifen a
während Modulinitialisierung, erhalten Sie eine AttributeError
erhalten.
Fügen Sie die folgende Zeile in a.py
:
print b.x
Dann wird die Ausgabe lautet:
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
File "a.py", line 4, in <module>
import b
File "/home/shlomme/tmp/x/b.py", line 2, in <module>
import a
File "/home/shlomme/tmp/x/a.py", line 7, in <module>
print b.x
AttributeError: 'module' object has no attribute 'x'
Das ist, weil Module auf Import und zu der Zeit ausgeführt wird b.x
zugegriffen wird, hat die Linie x = 3
noch nicht ausgeführt werden, die erst nach b out
passieren werden.
Wie andere Antworten beschreiben dieses Muster in Python akzeptabel ist:
def dostuff(self):
from foo import bar
...
, die die Ausführung der Import-Anweisung vermeiden, wenn die Datei von anderen Modulen importiert wird. Nur wenn es eine logische zirkuläre Abhängigkeit ist, wird dies nicht gelingen.
Die meisten Rund Importe sind nicht wirklich logisch Kreis Importe sondern ImportError
Fehler erhöhen, wegen der Art und Weise import()
Top-Level-Aussagen der gesamten Datei auswertet, wenn sie aufgerufen.
Diese können ImportErrors
fast immer vermieden werden, wenn Sie positiv Ihre Importe oben wollen :
Betrachten Sie diesen Kreis Import:
App A
# profiles/serializers.py
from images.serializers import SimplifiedImageSerializer
class SimplifiedProfileSerializer(serializers.Serializer):
name = serializers.CharField()
class ProfileSerializer(SimplifiedProfileSerializer):
recent_images = SimplifiedImageSerializer(many=True)
App B
# images/serializers.py
from profiles.serializers import SimplifiedProfileSerializer
class SimplifiedImageSerializer(serializers.Serializer):
title = serializers.CharField()
class ImageSerializer(SimplifiedImageSerializer):
profile = SimplifiedProfileSerializer()
Von David Beazleys ausgezeichneten Talk Module und Packages: Leben und sterben lassen! - PyCon 2015 , 1:54:00
, hier ist ein Weg, um mit Kreis Einfuhren in Python zu tun:
try:
from images.serializers import SimplifiedImageSerializer
except ImportError:
import sys
SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']
Dies versucht SimplifiedImageSerializer
zu importieren und wenn ImportError
angehoben wird, weil es bereits importiert wird, wird es von der importcache ziehen.
PS:. Sie haben die gesamte Post in David Beazley Stimme lesen
Ich habe hier ein Beispiel, das mir geschlagen!
foo.py
import bar
class gX(object):
g = 10
bar.py
from foo import gX
o = gX()
main.py
import foo
import bar
print "all done"
in der Befehlszeile: $ python main.py
Traceback (most recent call last):
File "m.py", line 1, in <module>
import foo
File "/home/xolve/foo.py", line 1, in <module>
import bar
File "/home/xolve/bar.py", line 1, in <module>
from foo import gX
ImportError: cannot import name gX
ich mit-Python-Antwort vollständig zustimmen hier. Aber ich habe auf einige Code gestolpert, die mit Kreis Importen und verursacht Probleme fehlerhaft war bei dem Versuch, Unit-Tests hinzuzufügen. So schnell es patchen, ohne alles ändern Sie das Problem beheben können einen dynamischen Import von tun.
# Hack to import something without circular import issue
def load_module(name):
"""Load module using imp.find_module"""
names = name.split(".")
path = None
for name in names:
f, path, info = imp.find_module(name, path)
path = [path]
return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")
Auch dies ist keine dauerhafte Lösung aber kann jemanden helfen, die ohne Änderung zu viel des Codes einen Importfehler beheben will.
Prost!
Modul a.py:
import b
print("This is from module a")
Modul b.py
import a
print("This is from module b")
Ausführen "Modul A" folgende Ausgabe:
>>>
'This is from module a'
'This is from module b'
'This is from module a'
>>>
Es Ausgang dieser 3 Zeilen, während es wegen Kreis Import zur Ausgabe infinitival soll. Was passiert, Zeile für Zeile während des Laufens „Modul A“ aufgeführt ist hier:
- Die erste Zeile ist
import b
. so wird es Modul b besuchen - Die erste Zeile im Modul b
import a
. so wird es besuchen, um ein Modul
- Die erste Zeile im Modul a
import b
aber beachten Sie, dass diese Zeile nicht wieder mehr ausgeführt werden , da jede Datei in Python eine Import Linie nur einmal ausführen , ist es egal, wo oder wann sie ausgeführt wird. so wird es in der nächsten Zeile und Druck"This is from module a"
passieren. - Nach dem Finish gesamten Modul eines von Modul b besucht, sind wir noch am Modul b. so dass die nächste Zeile druckt
"This is from module b"
- Modul b Zeilen werden vollständig ausgeführt. so werden wir wieder ein Modul, wo wir Modul b gestartet.
- Import b Linie bereits ausgeführt hat und nicht erneut ausgeführt werden. die nächste Zeile
"This is from module a"
gedruckt wird und das Programm wird beendet.
Rund Importe kann verwirrend sein, weil Import macht zwei Dinge:
- führt sie importierte Modul-Code
- fügt importierte Modul zu Modul globale Symboltabelle importieren
Der Erstere wird nur einmal durchgeführt, während diese bei jeder Import-Anweisung. Circular Import schafft Situation beim Import-Modul eines mit teilweise ausgeführten Code importiert verwendet. In der Folge sehen wird es keine Objekte nach dem Import-Anweisung erstellt. Im Folgenden Codebeispiel zeigt es.
Rund Importe sind nicht die ultimative böse auf jeden Fall vermieden werden. In einigen Frameworks wie Flask sind sie ganz natürlich und Ihren Code Tweaking zu eliminieren sie nicht der Code besser macht.
main.py
print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
print 'imports done'
print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)
b.by
print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"
a.py
print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"
Python main.py Ausgang mit Kommentaren
import b
b in, __name__ = b # b code execution started
b imports a
a in, __name__ = a # a code execution started
a imports b # b code execution is already in progress
b has x True
b has y False # b defines y after a import,
a out
b out
a in globals() False # import only adds a to main global symbol table
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available
Ich löste das Problem auf folgende Weise, und es funktioniert gut ohne Fehler.
Betrachten wir zwei Dateien a.py
und b.py
.
Ich habe diese a.py
und es funktionierte.
if __name__ == "__main__":
main ()
a.py:
import b
y = 2
def main():
print ("a out")
print (b.x)
if __name__ == "__main__":
main ()
b.py:
import a
print ("b out")
x = 3 + a.y
Der Ausgang ich erhalte, ist
>>> b out
>>> a out
>>> 5
Es gibt viele gute Antworten hier. Zwar gibt es in der Regel schnelle Lösungen für das Problem sind, von denen einige das Gefühl, mehr pythonic als andere, wenn Sie den Luxus zu tun, einige Umgestaltung haben, ein anderer Ansatz, um die Organisation des Codes zu analysieren und versuchen, die zirkuläre Abhängigkeit zu entfernen. Sie können zum Beispiel finden, dass Sie haben:
Datei a.py
from b import B
class A:
@staticmethod
def save_result(result):
print('save the result')
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
Datei b.py
from a import A
class B:
@staticmethod
def do_something_b_ish(param):
A.save_result(B.use_param_like_b_would(param))
In diesem Fall nur eine statische Methode in einer separaten Datei zu bewegen, sagen c.py
:
Datei c.py
def save_result(result):
print('save the result')
die save_result
Methode von einem Entfernen ermöglichen, und ermöglicht es somit, die Einfuhr von A aus einer Entfernung von in b:
Überarbeitete Datei a.py
from b import B
from c import save_result
class A:
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
Überarbeitete Datei b.py
from c import save_result
class B:
@staticmethod
def do_something_b_ish(param):
save_result(B.use_param_like_b_would(param))
Zusammenfassend: Wenn Sie ein Werkzeug (z Pylint oder PyCharm), die auf Methoden berichtet, die statisch sein kann, nur einen staticmethod
Dekorateur auf sie werfen vielleicht nicht der beste Weg, um die Warnung zu unterdrücken. Obwohl die Methode der Klasse verwandt zu sein scheint, könnte es besser sein, es zu trennen, vor allem, wenn Sie mehrere eng verwandte Module, die die gleiche Funktionalität benötigen und Sie beabsichtigen, DRY Prinzipien zu üben.
Dies könnte eine andere Lösung sein, für mich gearbeitet.
def MandrillEmailOrderSerializer():
from sastaticketpk.apps.flights.api.v1.serializers import MandrillEmailOrderSerializer
return MandrillEmailOrderSerializer
email_data = MandrillEmailOrderSerializer()(order.booking).data
Ok, ich glaube, ich habe eine ziemlich coole Lösung.
Angenommen, Sie Datei a
und Datei b
haben.
Sie haben eine def
oder eine class
in Datei b
, die Sie im Modul a
verwenden möchten, aber Sie haben etwas anderes, entweder eine def
, class
oder Variable aus Datei a
, die Sie in Ihrer Definition oder Klasse in der Datei b
benötigen.
Was können Sie tun, ist, am unteren Rand der Datei a
, nachdem die Funktion oder Klasse in der Datei a
aufrufen, die in der Datei b
benötigt wird, aber bevor die Funktion oder Klasse aus Datei b
aufrufen, die Sie für die Datei a
benötigen, sagen import b
Dann, und hier ist die Schlüsselteil , in alle Definitionen oder Klassen in der Datei b
, die die def
oder class
von Datei a
benötigen (nennen wir es CLASS
), sagen Sie from a import CLASS
Das funktioniert, weil Sie Datei b
ohne Python ausführen eine der Import-Anweisungen in der Datei b
importieren können und somit entziehen Sie Kreis Importe.
Zum Beispiel:
Datei ein:
class A(object):
def __init__(self, name):
self.name = name
CLASS = A("me")
import b
go = B(6)
go.dostuff
Datei b:
class B(object):
def __init__(self, number):
self.number = number
def dostuff(self):
from a import CLASS
print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."
Voila.