Więc otrzymuję ten błąd
Traceback (most recent call last):
File "/Users/alex/dev/runswift/utils/sim2014/simulator.py", line 3, in <module>
from world import World
File "/Users/alex/dev/runswift/utils/sim2014/world.py", line 2, in <module>
from entities.field import Field
File "/Users/alex/dev/runswift/utils/sim2014/entities/field.py", line 2, in <module>
from entities.goal import Goal
File "/Users/alex/dev/runswift/utils/sim2014/entities/goal.py", line 2, in <module>
from entities.post import Post
File "/Users/alex/dev/runswift/utils/sim2014/entities/post.py", line 4, in <module>
from physics import PostBody
File "/Users/alex/dev/runswift/utils/sim2014/physics.py", line 21, in <module>
from entities.post import Post
ImportError: cannot import name Post
i widzisz, że używam tej samej instrukcji importu w dalszej części i działa? Czy jest jakaś niepisana zasada dotycząca importowania cyklicznego? Jak używać tej samej klasy niżej w stosie wywołań?
from
zawsze będzie działać. Jeśli mamclass A(object): pass; class C(b.B): pass
w module a iwclass B(a.A): pass
module b, import cykliczny nadal stanowi problem i to nie zadziała.B
do modułua
lub przenieś klasęC
do modułu,b
aby przerwać cykl. Warto również zauważyć, że nawet jeśli tylko jeden kierunek okręgu ma kod najwyższego poziomu zaangażowania (np jeśli klasaC
nie istnieje), to może się błąd, w zależności od modułu został importowany pierwszy przez inny kod.from . import sibling_module
Niefrom .sibling_module import SomeClass
). Jest trochę więcej subtelności, gdy plik pakietu__init__.py
jest zaangażowany w import cykliczny, ale problem jest zarówno rzadki, jak i prawdopodobnie błądimport
implementacji. Zobacz błąd w Pythonie 23447 , dla którego przesłałem poprawkę (która niestety nie działa).Kiedy importujesz moduł (lub jego element członkowski) po raz pierwszy, kod wewnątrz modułu jest wykonywany sekwencyjnie, jak każdy inny kod; np. nie jest traktowany inaczej niż ciało funkcji.
import
Jest tylko komenda jak każdy inny (cesji, wywołania funkcji,def
,class
). Zakładając, że importowanie odbywa się u góry skryptu, oto co się dzieje:World
zworld
Theworld
skrypt zostaje wykonany.world
import skryptówField
, co powodujeentities.field
skrypt zostanie wykonany.entities.post
skryptu, ponieważ próbowałeś zaimportowaćPost
entities.post
przyczyny scriptphysics
moduł ma zostać wykonany, ponieważ stara się importemPostBody
physics
próbuje importowaćPost
zentities.post
entities.post
moduł istnieje w pamięci, ale to naprawdę nie ma znaczenia. Albo modułu nie ma w pamięci, albo moduł nie ma jeszczePost
członka, ponieważ nie zakończył wykonywania definiowaniaPost
Post
nie ma go do zaimportowaniaWięc nie, to nie jest „praca wyżej w stosie wywołań”. To jest ślad stosu wskazujący, gdzie wystąpił błąd, co oznacza, że wystąpił błąd podczas próby importu
Post
w tej klasie. Nie należy używać importu cyklicznego. W najlepszym przypadku przynosi znikome korzyści (zazwyczaj brak korzyści) i powoduje takie problemy. Obciąża to każdego dewelopera w utrzymaniu go, zmuszając go do chodzenia po skorupkach jaj, aby ich nie rozbić. Zmień organizację modułów.źródło
isinstance(userData, Post)
. Niezależnie od tego nie masz wyboru. Import cykliczny nie zadziała. Fakt, że masz import okrężny, to dla mnie zapach kodu. Sugeruje, że masz pewne funkcje, które powinny zostać przeniesione do trzeciego modułu. Nie mogłem powiedzieć czego, nie patrząc na obie klasy.def
nie jest wykonywana, dopóki funkcja nie zostanie wywołana, więc import nie nastąpi, dopóki nie wywołasz funkcji. Do tego czasuimport
s powinny działać, ponieważ jeden z modułów zostałby całkowicie zaimportowany przed wywołaniem. To absolutnie obrzydliwy hack i nie powinien pozostawać w bazie twojego kodu przez dłuższy czas.import foo
zamiastfrom foo import Bar
. Dzieje się tak, ponieważ większość modułów po prostu definiuje rzeczy (takie jak funkcje i klasy), które będą uruchamiane później. Moduły, które wykonują ważne rzeczy podczas ich importowania (takie jak skrypt niechroniony przezif __name__ == "__main__"
), mogą nadal powodować problemy, ale nie jest to zbyt częste.Aby zrozumieć zależności cykliczne, należy pamiętać, że Python jest zasadniczo językiem skryptowym. Wykonywanie instrukcji poza metodami odbywa się w czasie kompilacji. Instrukcje importu są wykonywane tak jak wywołania metod i aby je zrozumieć, należy myśleć o nich jak o wywołaniach metod.
Podczas importowania to, co się stanie, zależy od tego, czy importowany plik już istnieje w tabeli modułu. Jeśli tak, Python używa tego, co aktualnie znajduje się w tablicy symboli. Jeśli nie, Python zaczyna czytać plik modułu, kompilując / wykonując / importując wszystko, co tam znajdzie. Symbole, do których odwołuje się w czasie kompilacji, zostały znalezione lub nie, w zależności od tego, czy zostały one zauważone, czy nie zostały jeszcze zauważone przez kompilator.
Wyobraź sobie, że masz dwa pliki źródłowe:
Plik X.py
def X1: return "x1" from Y import Y2 def X2: return "x2"
Plik Y.py
def Y1: return "y1" from X import X1 def Y2: return "y2"
Teraz załóżmy, że kompilujesz plik X.py. Kompilator rozpoczyna od zdefiniowania metody X1, a następnie trafia na instrukcję import w X.py. Powoduje to, że kompilator wstrzymuje kompilację X.py i rozpoczyna kompilację Y.py. Wkrótce potem kompilator trafia na instrukcję import w Y.py. Ponieważ X.py jest już w tabeli modułów, Python używa istniejącej niekompletnej tabeli symboli X.py, aby spełnić wszelkie wymagane odniesienia. Wszelkie symbole pojawiające się przed instrukcją import w X.py znajdują się teraz w tablicy symboli, ale żadne symbole po nich już nie. Ponieważ X1 pojawia się teraz przed instrukcją importu, zostało pomyślnie zaimportowane. Python wznawia kompilację Y.py. Robiąc to, definiuje Y2 i kończy kompilację Y.py. Następnie wznawia kompilację X.py i znajduje Y2 w tabeli symboli Y.py. Kompilacja ostatecznie kończy się bez błędu.
Coś zupełnie innego dzieje się, jeśli spróbujesz skompilować Y.py z wiersza poleceń. Podczas kompilowania Y.py kompilator trafia w instrukcję import, zanim zdefiniuje Y2. Następnie zaczyna kompilować X.py. Wkrótce trafia na instrukcję import w X.py, która wymaga Y2. Ale Y2 jest niezdefiniowane, więc kompilacja kończy się niepowodzeniem.
Zwróć uwagę, że jeśli zmodyfikujesz X.py, aby zaimportować Y1, kompilacja zawsze się powiedzie, bez względu na to, który plik kompilujesz. Jednak jeśli zmodyfikujesz plik Y.py, aby zaimportować symbol X2, żaden plik nie zostanie skompilowany.
Za każdym razem, gdy moduł X lub jakikolwiek moduł importowany przez X może zaimportować bieżący moduł, NIE używaj:
from X import Y
Za każdym razem, gdy myślisz, że może nastąpić cykliczny import, powinieneś również unikać kompilacji odniesień do zmiennych w innych modułach. Rozważ niewinnie wyglądający kod:
import X z = X.Y
Załóżmy, że moduł X importuje ten moduł, zanim ten moduł zaimportuje X. Ponadto przypuśćmy, że Y jest zdefiniowane w X po instrukcji importu. Wtedy Y nie zostanie zdefiniowane podczas importowania tego modułu i pojawi się błąd kompilacji. Jeśli ten moduł zaimportuje Y jako pierwszy, możesz sobie z tym poradzić. Ale kiedy jeden z twoich współpracowników niewinnie zmieni kolejność definicji w trzecim module, kod się zepsuje.
W niektórych przypadkach można rozwiązać zależności cykliczne, przenosząc instrukcję importu w dół poniżej definicji symboli wymaganych przez inne moduły. W powyższych przykładach definicje przed instrukcją import nigdy nie zawodzą. Definicje po instrukcji import czasami zawodzą, w zależności od kolejności kompilacji. Możesz nawet umieścić instrukcje importu na końcu pliku, o ile żaden z importowanych symboli nie jest potrzebny w czasie kompilacji.
Zwróć uwagę, że przeniesienie instrukcji importu w dół w module przesłania to, co robisz. Skompensuj to komentarzem u góry modułu, podobnym do następującego:
#import X (actual import moved down to avoid circular dependency)
Ogólnie jest to zła praktyka, ale czasami trudno jej uniknąć.
źródło
Dla tych z Was, którzy, tak jak ja, przychodzą do tego numeru z Django, powinniście wiedzieć, że dokumentacja zapewnia rozwiązanie: https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey
„... Aby odwołać się do modeli zdefiniowanych w innej aplikacji, możesz jawnie określić model z pełną etykietą aplikacji. Na przykład, jeśli powyższy model producenta jest zdefiniowany w innej aplikacji zwanej produkcją, musisz użyć:
class Car(models.Model): manufacturer = models.ForeignKey( 'production.Manufacturer', on_delete=models.CASCADE, )
Ten rodzaj odwołania może być przydatny podczas rozwiązywania zależności między importami cyklicznymi między dwiema aplikacjami.… ”
źródło
Udało mi się zaimportować moduł w ramach funkcji (tylko), który wymagałby obiektów z tego modułu:
def my_func(): import Foo foo_instance = Foo()
źródło
Jeśli napotkasz ten problem w dość złożonej aplikacji, refaktoryzacja wszystkich importów może być uciążliwa. PyCharm oferuje szybką naprawę, która automatycznie zmieni również użycie importowanych symboli.
źródło
Używałem następujących:
from module import Foo foo_instance = Foo()
ale żeby się go pozbyć
circular reference
zrobiłem co następuje i zadziałało:import module.foo foo_instance = foo.Foo()
źródło