Python circulair importeren?

Dus ik krijg deze foutmelding

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

en je kunt zien dat ik hetzelfde importstatement verder gebruik en dat het werkt? Bestaat er een ongeschreven regel over circulair importeren? Hoe gebruik ik dezelfde klasse verderop in de call-stack?


Antwoord 1, autoriteit 100%

Ik denk dat het antwoord van jpmc26, hoewel zeker niet fout, ook neerkomt zwaar op circulaire import. Ze kunnen prima werken, als je ze correct instelt.

De eenvoudigste manier om dit te doen is door de syntaxis import my_modulete gebruiken, in plaats van from my_module import some_object. De eerste zal bijna altijd werken, zelfs als my_moduleons terug importeert. Dit laatste werkt alleen als my_objectal is gedefinieerd in my_module, wat in een circulaire import mogelijk niet het geval is.

Om specifiek te zijn voor uw geval: probeer entities/post.pyte wijzigen om import physicste doen en raadpleeg dan liever physics.PostBodydan alleen PostBodyrechtstreeks. Verander op dezelfde manier physics.pyom import entities.postte importeren en gebruik vervolgens entities.post.Postin plaats van alleen Post.


Antwoord 2, autoriteit 30%

Wanneer u een module (of een lid ervan) voor de eerste keer importeert, wordt de code in de module sequentieel uitgevoerd zoals elke andere code; het wordt bijvoorbeeld niet anders behandeld dan het lichaam van een functie. Een importis gewoon een commando zoals elk ander (toewijzing, een functieaanroep, def, class). Ervan uitgaande dat uw invoer bovenaan het script plaatsvindt, gebeurt er het volgende:

  • Wanneer u worldprobeert te importeren uit world, wordt het world-script uitgevoerd.
  • Het world-script importeert Field, waardoor het entities.field-script wordt uitgevoerd.
  • Dit proces gaat door totdat u het script import entities.postbereikt omdat u probeerde Post
  • te importeren

  • Het script import entities.postzorgt ervoor dat de module physicswordt uitgevoerd omdat het probeert PostBody
  • te importeren

  • Ten slotte probeert physicsPostte importeren van import entities.post
  • Ik weet niet zeker of de module import entities.postal in het geheugen bestaat, maar dat maakt niet uit. Ofwel de module bevindt zich niet in het geheugen, of de module heeft nog geen Postlid omdat het niet klaar is met uitvoeren om Postte definiëren
  • Er treedt hoe dan ook een fout op omdat Poster niet is om te importeren

Dus nee, het is niet “verder werken in de oproepstack”. Dit is een stapelspoor van waar de fout is opgetreden, wat betekent dat deze wordt uitgeschakeld om te importeren Postin die klasse. U moet geen circulaire invoer gebruiken. Op zijn best heeft het een verwaarloosbare uitkering (typisch, nee profiteren), en het veroorzaakt problemen zoals deze. Het lasten elke ontwikkelaar die het onderhoudt, waardoor ze op eierschalen lopen om het te voorkomen. Refactor Uw Module-organisatie.


Antwoord 3, Autoriteit 23%

Om cirkelvormige afhankelijkheden te begrijpen, moet u onthouden dat Python in essentie een scripttaal is. Uitvoering van uitspraken buiten methoden vindt plaats bij compileertijd. Importverklaringen worden uitgevoerd net als methode-oproepen en om ze te begrijpen, moet u aan hen denken zoals methode-oproepen.

Wanneer u een importeert, wat er gebeurt, hangt af van het feit of het bestand dat u importeert al in de moduletafel bestaat. Als het dat doet, gebruikt Python alles wat momenteel in de symbooltafel bevindt. Zo niet, dan begint Python het module-bestand te lezen, het opstellen / uitvoeren / importeren wat het daar vindt. Symbolen waarnaar bij compileertijd wordt verwezen, worden of niet gevonden, afhankelijk van of ze zijn gezien, of nog te zien zijn door de compiler.

Stel je voor dat je twee bronbestanden hebt:

bestand x.py

def X1:
    return "x1"
from Y import Y2
def X2:
    return "x2"

bestand y.py

def Y1:
    return "y1"
from X import X1
def Y2:
    return "y2"

Stel nu dat u bestand X.py. De compiler begint met het definiëren van de methode X1 en raakt vervolgens de invoerverklaring in X.PY. Dit zorgt ervoor dat de compiler compilatie van x.py pauzeert en y.py. Py. Kort daarna raakt de compiler de invoerverklaring in Y.PY. Omdat X.py al in de moduletafel staat, gebruikt Python de bestaande onvolledige X.py-symbooltabel om aan alle gevraagde referenties te voldoen. Alle symbolen die verschijnen vóór de invoerinstructie in X.py bevinden zich nu in de symbooltafel, maar er zijn geen symbolen daarna. Aangezien X1 nu vóór de invoerverklaring verschijnt, wordt deze met succes geïmporteerd. Python hervat vervolgens het compileren van y.py. Daarbij definieert het Y2 en eindigt het compileren y.py. Het hervat vervolgens de compilatie van x.py en vindt y2 in de Y.py-symbooltafel. Compilatie voltooit uiteindelijk W / O-fout.

Er gebeurt iets heel anders als u probeert om y.py uit de opdrachtregel te compileren. Tijdens het compileren van y.py, raakt de compiler de invoerverklaring voordat deze Y2 definieert. Dan begint het X.py te compileren. Binnenkort raakt het de invoerverklaring in X.py dat Y2 vereist. Maar Y2 is ongedefinieerd, dus de compileer mislukt.

Houd er rekening mee dat als u X.py gebruikt om Y1 te importeren, de compileer altijd zal slagen, ongeacht welk bestand u compileert. Als u echter bestand y.py gebruikt om symbool X2 te importeren, zal geen van beide bestand compileren.

op elk moment wanneer module x, of een module geïmporteerd door x de huidige module kan importeren, niet gebruiken:

from X import Y

Elke keer dat u denkt dat er mogelijk een cirkelvormige import is, moet u ook vermijden dat compileren tijdverwijzingen naar variabelen in andere modules moeten vermijden. Overweeg de onschuldige looking code:

import X
z = X.Y

Stel dat module X deze module importeert voordat deze module X importeert. Stel verder dat Y is gedefinieerd in X na het importstatement. Dan wordt Y niet gedefinieerd wanneer deze module wordt geïmporteerd en krijgt u een compileerfout. Als deze module eerst Y importeert, kun je ermee wegkomen. Maar wanneer een van uw collega’s onschuldig de volgorde van definities in een derde module verandert, zal de code breken.

In sommige gevallen kunt u circulaire afhankelijkheden oplossen door een importinstructie naar beneden te verplaatsen onder de symbooldefinities die andere modules nodig hebben. In de bovenstaande voorbeelden falen definities vóór de importinstructie nooit. Definities na de importinstructie mislukken soms, afhankelijk van de volgorde van compilatie. U kunt zelfs importinstructies aan het einde van een bestand plaatsen, zolang u geen van de geïmporteerde symbolen nodig heeft tijdens het compileren.

Houd er rekening mee dat het naar beneden verplaatsen van importinstructies in een module verdoezelt wat u aan het doen bent. Compenseer dit met een opmerking bovenaan je module, zoiets als het volgende:

#import X   (actual import moved down to avoid circular dependency)

Over het algemeen is dit een slechte gewoonte, maar soms is het moeilijk te vermijden.


Antwoord 4, autoriteit 11%

Voor degenen onder u die, net als ik, op dit probleem van Django komen, moet u weten dat de documenten een oplossing bieden:
https://docs.djangoproject.com/en/1.10/ref/ modellen/velden/#foreignkey

“…Om te verwijzen naar modellen die in een andere toepassing zijn gedefinieerd, kunt u expliciet een model specificeren met het volledige toepassingslabel. Als het bovenstaande fabrikantmodel bijvoorbeeld is gedefinieerd in een andere toepassing met de naam productie, moet u :

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'production.Manufacturer',
        on_delete=models.CASCADE,
)

Dit soort verwijzing kan handig zijn bij het oplossen van circulaire importafhankelijkheden tussen twee applicaties.…”


Antwoord 5, autoriteit 4%

Ik was in staat om de module te importeren binnen de functie (alleen) die de objecten uit deze module zou vereisen:

def my_func():
    import Foo
    foo_instance = Foo()

Antwoord 6, autoriteit 2%

Als je dit probleem tegenkomt in een redelijk complexe app, kan het lastig zijn om al je imports te refactoren. PyCharm biedt hiervoor een quickfix die automatisch ook al het gebruik van de geïmporteerde symbolen zal veranderen.


Antwoord 7

Ik gebruikte het volgende:

from module import Foo
foo_instance = Foo()

maar om van circular referenceaf te komen, deed ik het volgende en het werkte:

import module.foo
foo_instance = foo.Foo()

Other episodes