Czy ktoś może wyjaśnić względne importy Pythona?

174

Za całe życie nie mogę uruchomić względnego importu Pythona. Stworzyłem prosty przykład, gdzie to nie działa:

Struktura katalogów jest następująca:

/__init__.py
/start.py
/parent.py
/sub/__init__.py
/sub/relative.py

/start.py zawiera tylko: import sub.relative

/sub/relative.py zawiera tylko from .. import parent

Wszystkie inne pliki są puste.

Podczas wykonywania następujących poleceń w wierszu poleceń:

$ cd /
$ python start.py

Dostaję:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/home/cvondrick/sandbox/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: Attempted relative import beyond toplevel package

Używam Pythona 2.6. Dlaczego tak się dzieje? Jak sprawić, by ten przykład piaskownicy działał?

Carl
źródło

Odpowiedzi:

140

Importujesz z pakietu „sub”. start.pynie jest sobą w paczce, nawet jeśli jest __init__.pyprezent.

Musisz uruchomić swój program z jednego katalogu parent.py:

./start.py

./pkg/__init__.py
./pkg/parent.py
./pkg/sub/__init__.py
./pkg/sub/relative.py

Z start.py:

import pkg.sub.relative

Teraz pkg jest pakietem najwyższego poziomu i twój import względny powinien działać.


Jeśli chcesz pozostać przy obecnym układzie, możesz po prostu użyć import parent. Ponieważ używasz start.pydo uruchamiania interpretera, katalog, w którym start.pysię znajduje, znajduje się w ścieżce Pythona. parent.pymieszka tam jako oddzielny moduł.

Możesz także bezpiecznie usunąć najwyższy poziom __init__.py, jeśli nie zaimportujesz niczego do skryptu znajdującego się dalej w drzewie katalogów.

ebo
źródło
2
Mylisz pojęcia „moduł” i „pakiet”. „start.py” reprezentuje moduł, „start”, „mod” i „mod.sub” to pakiety, „mod” to pakiet najwyższego poziomu.
Ferdinand Beyer
34
Dzięki, ale szczerze mówiąc, wydaje się to naprawdę głupie. Jak na tak piękny język, nie mogę uwierzyć, że projektanci stworzyliby takie ograniczenie. Czy nie ma innego sposobu?
Carl
2
To wcale nie jest głupie. Importy względne oznaczają odwoływanie się do modułów siostrzanych w pakiecie. Jeśli chcesz zaimportować moduł najwyższego poziomu, użyj importu bezwzględnego.
Ferdinand Beyer
58
Nie głupie? Więc w bash, nie byłbyś w stanie wskazać względnego wyższego katalogu za pomocą "..", nie przeszkadzałoby ci?
e-satis
2
Wydaje mi się, że pomysłem Pythona jest użycie importu „absolutnego” z katalogu, w którym uruchomiłeś skrypt nadrzędny. Możesz więc użyć ścieżki bezwzględnej „import parent”, aby zaimportować moduł nadrzędny z rodzica. A relatywny import jakiegoś rodzaju spuścizny lub cokolwiek innego ...
Odyseusz
35

Jeśli masz zamiar dzwonić relative.pybezpośrednio, tj. Jeśli naprawdę chcesz importować z modułu najwyższego poziomu, musisz jawnie dodać go do sys.pathlisty.
Oto jak to powinno działać:

# Add this line to the beginning of relative.py file
import sys
sys.path.append('..')

# Now you can do imports from one directory top cause it is in the sys.path
import parent

# And even like this:
from parent import Parent

Jeśli uważasz, że powyższe może powodować pewną niespójność, możesz zamiast tego użyć tego:

sys.path.append(sys.path[0] + "/..")

sys.path[0] odnosi się do ścieżki, z której został uruchomiony punkt wejścia.

AmirHossein
źródło
3

Sprawdzanie w python3:

python -V
Python 3.6.5

Przykład 1:

.
├── parent.py
├── start.py
└── sub
    └── relative.py

- start.py
import sub.relative

- parent.py
print('Hello from parent.py')

- sub/relative.py
from .. import parent

Jeśli uruchomimy to w ten sposób (aby upewnić się, że PYTHONPATH jest pusta):

PYTHONPATH='' python3 start.py

Wynik:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/python-import-examples/so-example-v1/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Jeśli zmienimy import w sub/relative.py

- sub/relative.py
import parent

Jeśli uruchomimy to w ten sposób:

PYTHONPATH='' python3 start.py

Wynik:

Hello from parent.py

Przykład 2:

.
├── parent.py
└── sub
    ├── relative.py
    └── start.py

- parent.py
print('Hello from parent.py')

- sub/relative.py
print('Hello from relative.py')

- sub/start.py
import relative
from .. import parent

Uruchom to jak:

PYTHONPATH='' python3 sub/start.py

Wynik:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 2, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Jeśli zmienimy import w sub/start.py:

- sub/start.py
import relative
import parent

Uruchom to jak:

PYTHONPATH='' python3 sub/start.py

Wynik:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 3, in <module>
    import parent
ModuleNotFoundError: No module named 'parent'

Uruchom to jak:

PYTHONPATH='.' python3 sub/start.py

Wynik:

Hello from relative.py
Hello from parent.py

Lepiej też skorzystać z importu z katalogu głównego, czyli:

- sub/start.py
import sub.relative
import parent

Uruchom to jak:

PYTHONPATH='.' python3 sub/start.py

Wynik:

Hello from relative.py
Hello from parent.py
mrgloom
źródło