Vraag Ultiem antwoord op relatieve pythonimporten


Ik weet dat er veel vragen zijn over dezelfde importproblemen in Python, maar het lijkt erop dat niemand erin geslaagd is om een ​​duidelijk voorbeeld van correct gebruik te geven.

Laten we zeggen dat we een pakket hebben mypackage met twee modules foo en bar. Binnen foo we moeten toegang hebben bar.

Omdat we het nog steeds aan het ontwikkelen zijn, mypackage is niet in sys.path.

We willen in staat zijn om:

  • importeren mypackage.foo
  • rennen foo.py als een script en voer het voorbeeldgebruik of de tests uit vanaf het __main__ sectie.
  • gebruik Python 2.5

Hoe moeten we de import in foo.py doen om er zeker van te zijn dat het in al deze gevallen werkt.

# mypackage/__init__.py
...

# mypackage/foo/__init__.py
...

# mypackage/bar.py  
def doBar()
    print("doBar")

# mypackage/foo/foo.py
import bar # fails with module not found
import .bar #fails due to ValueError: Attempted relative import in non-package

def doFoo():
    print(doBar())

if __name__ == '__main__':
    doFoo()

10
2017-11-28 16:49


oorsprong


antwoorden:


Bekijk de volgende informatie van PEP 328:

Relatieve importen gebruiken een module __name__ attribuut om de positie van die module in de pakkethiërarchie te bepalen. Als de naam van de module geen pakketinformatie bevat (deze is bijvoorbeeld ingesteld op '__main__') Vervolgens worden relatieve importen opgelost alsof de module een topniveau-module zou zijn, ongeacht waar de module zich feitelijk in het bestandssysteem bevindt.

Wanneer je rent foo.py als een script, die module __name__ is '__main__', dus je kunt geen relatieve invoer doen. Dit zou waar zijn, zelfs als mypackage was aan sys.path. In principe kunt u alleen relatieve invoer vanuit een module uitvoeren als die module is geïmporteerd.

Hier zijn een paar opties om hier omheen te werken:

1 in foo.py, controleer of __name__ == '__main__' en voorwaardelijk toevoegen mypackage naar sys.path:

if __name__ == '__main__':
    import os, sys
    # get an absolute path to the directory that contains mypackage
    foo_dir = os.path.dirname(os.path.join(os.getcwd(), __file__))
    sys.path.append(os.path.normpath(os.path.join(foo_dir, '..', '..')))
    from mypackage import bar
else:
    from .. import bar

2) Altijd importeren bar gebruik makend van from mypackage import bar, en voer uit foo.py op zo'n manier dat mypackage is automatisch zichtbaar:

$ cd <path containing mypackage>
$ python -m mypackage.foo.foo

20
2017-11-28 18:15



Mijn oplossing ziet er wat schoner uit en kan bovenaan komen, met alle andere invoer:

try:
   from foo import FooClass
except ModuleNotFoundError:
   from .foo import FooClass

2
2017-08-03 15:38