ValueError: попытка относительного импорта за пределы пакета верхнего уровня

Я играл в систему импорта Python, чтобы лучше понять, как это работает, и я столкнулся с другой проблемой. У меня есть следующая структура

pkg/
    __init__.py
    c.py
    d.py

    subpkg/
        __init__.py
        a.py
        b.py

Внутри a.py У меня есть следующий код:

from . import b
from .. import d

И внутри c.py у меня есть следующее:

import subpkg.a

Теперь я получаю следующую ошибку:

ValueError: попытка относительного импорта за пределы пакета верхнего уровня

Но почему? Как я могу это решить? Я запускаю c.py из IDLE, а pkg следует рассматривать как пакет, так как он имеет файл __init__.py.

Первый импорт работает отлично, но это не работает:

from .. import d

Потому что я пытаюсь импортировать что-то из родительского пакета, но, по-видимому, я не могу, по какой-то странной причине.

Ответ 1

Python 3 изменил систему импорта, поэтому каждый раз, когда вам нужен модуль, который находится рядом с тем, с которым вы работаете, вам необходим относительный импорт (если только вы не sys.path с PYTHONPATH или sys.path).

Правильное использование здесь должно быть

from .subpkg import a

Когда вы работаете с IDLE, у вас совершенно другая среда. Таким образом, вы можете добавить текущее местоположение к вашему пути, чтобы импорт снова работал.

пытаться:

sys.path.insert(0, '')

Это может быть странно, но для большего блага

PS: Если эта последняя вещь не работает - у меня сейчас нет среды IDLE - возможно, потому, что рабочий каталог установлен неправильно.

Попробуйте вместо этого ответ: fooobar.com/questions/261782/...

Ответ 2

Это заставило меня усомниться в моем безумии.

Проблема связана с тем, что люди ошибочно воспринимают относительный импорт как относительный путь, а это не так.

Относительный импорт зависит от местоположения запускаемого файла.

Этот ответ более подробно объясняет, как на самом деле работают модули python, но суммирует.

  1. Когда файл загружен, ему присваивается имя:
    • Если он был загружен как скрипт верхнего уровня (запускается напрямую), его имя - __main__.
    • Если он был загружен как модуль (с импортом), его именем является имя файла, которому предшествуют имена любых пакетов/подпакетов, частью которых он является, разделенных точками - pkg.subpkg.a
  2. Если вы делаете from .., в имени файла должно быть как минимум 2 точки. from ... - 3 точки.

Теперь самое интересное.

Если вы запускаете c.py напрямую, то ему присваивается имя __main__, а a.py имеет subpkg.a.

Согласно второму утверждению, у вас должно быть как минимум 2 точки на имя subpkg.a для запуска from .. внутри него.

Исправление

Создайте новый файл вне pkg, скажем, main.py

pkg/
    __init__.py
    c.py
    d.py

    subpkg/
        __init__.py
        a.py
        b.py
main.py

Внутри main.py

import pkg.c

Если мы запустим main.py, он получит имя __main__, а a.py get pkg.subpkg.a. Согласно второму утверждению, у него теперь есть 2 точки в имени, и мы можем сделать from ..

Еще кое-что. Теперь, когда c.py загружен как модуль, мы должны использовать from для загрузки a.py.

from .subpkg import a

Ответ 3

Я нашел это решение:

#! /usr/bin/env python
import os
import sys
sys.path.append(os.path.realpath('.'))
from d import *