Чи може хтось пояснити відносний імпорт пітона?


174

Я не можу за все життя отримати відносний імпорт пітона на роботу. Я створив простий приклад того, де це не працює:

Структура каталогу:

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

/start.py містить просто: import sub.relative

/sub/relative.py містить просто from .. import parent

Усі інші файли порожні.

Виконуючи наступне в командному рядку:

$ cd /
$ python start.py

Я отримав:

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

Я використовую Python 2.6. Чому це так? Як змусити цей приклад роботи пісочниці?

Відповіді:


140

Ви імпортуєте з пакета "sub". start.pyне є самою в пакеті, навіть якщо є __init__.pyподарунок.

Вам потрібно буде запустити свою програму з одного каталогу parent.py:

./start.py

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

З start.py:

import pkg.sub.relative

Тепер pkg - це пакет верхнього рівня, і ваш відносний імпорт повинен працювати.


Якщо ви хочете дотримуватися свого поточного макета, ви можете просто скористатися import parent. Оскільки ви використовуєте start.pyдля запуску інтерпретатора, каталог, де start.pyвін знаходиться, знаходиться у вашому шляху python. parent.pyживе там як окремий модуль.

Ви також можете безпечно видалити верхній рівень __init__.py, якщо ви нічого не імпортуєте в сценарій, далі в дерево каталогів.


2
Ви плутаєте терміни "модуль" та "пакет". 'start.py' являє собою модулі 'start', 'mod' і 'mod.sub' - це пакети, 'mod' - пакет верхнього рівня.
Фердинанд Бейєр

34
Дякую, але це чесно здається справді нерозумним. Для такої красивої мови, я не можу повірити, що дизайнери створили б таке обмеження. Хіба немає іншого способу?
Карл

2
Це зовсім не дурно. Відносний імпорт - це засіб, що стосується модулів, які знаходяться в середині пакета. Якщо потрібно імпортувати модуль верхнього рівня, використовуйте абсолютний імпорт.
Фердинанд Бейєр

58
Не дурний? Так що в баші, не вдалося б звернутися до відносного верхнього режиму з "..", не турбувало б вас?
e-satis

2
Мені здається, що ідея пітона полягає у використанні "абсолютного" імпорту з каталогу, де ви запустили свій батьківський скрипт. Таким чином, ви можете використовувати абсолютний шлях "імпортувати батьків" для імпорту батьківського модуля з побратимів. І відносний імпорт якоїсь спадщини чи що завгодно ..
Одіссей

35

Якщо ви збираєтесь зателефонувати relative.pyбезпосередньо, тобто, якщо ви дійсно хочете імпортувати з модуля верхнього рівня, вам потрібно явно додати його до sys.pathсписку.
Ось як це має працювати:

# 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

Якщо ви думаєте, що вищесказане може викликати певну непослідовність, ви можете скористатися цим:

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

sys.path[0] відноситься до шляху, по якому проходила точка входу.


3

Перевірка в python3:

python -V
Python 3.6.5

Приклад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

Якщо ми запустимо його так (просто щоб переконатися, що PYTHONPATH порожній):

PYTHONPATH='' python3 start.py

Вихід:

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

Якщо ми змінимо імпорт в sub/relative.py

- sub/relative.py
import parent

Якщо ми запустимо його так:

PYTHONPATH='' python3 start.py

Вихід:

Hello from parent.py

Приклад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

Виконайте це так:

PYTHONPATH='' python3 sub/start.py

Вихід:

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

Якщо ми змінимо імпорт у sub/start.py:

- sub/start.py
import relative
import parent

Виконайте це так:

PYTHONPATH='' python3 sub/start.py

Вихід:

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

Виконайте це так:

PYTHONPATH='.' python3 sub/start.py

Вихід:

Hello from relative.py
Hello from parent.py

Також краще використовувати імпорт із кореневої папки, тобто:

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

Виконайте це так:

PYTHONPATH='.' python3 sub/start.py

Вихід:

Hello from relative.py
Hello from parent.py
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.