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ł?

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.


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.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.