Подтвердить что ты не робот

За ошибкой пакета верхнего уровня в относительном импорте

Кажется, здесь уже есть несколько вопросов об относительном импорте в Python 3, но, пройдя многие из них, я так и не нашел ответа на свой вопрос. так вот в чем вопрос.

У меня есть пакет, показанный ниже

package/
   __init__.py
   A/
      __init__.py
      foo.py
   test_A/
      __init__.py
      test.py

и у меня есть одна строка в test.py:

from ..A import foo

Теперь я нахожусь в папке package, и я бегу

python -m test_A.test

Я получил сообщение

"ValueError: attempted relative import beyond top-level package"

но если я нахожусь в родительской папке package, например, я запускаю:

cd ..
python -m package.test_A.test

Все отлично.

Теперь мой вопрос: когда я нахожусь в папке package, и я запускаю модуль внутри подпакета test_A как test_A.test, исходя из моего понимания, ..A поднимается только на один уровень, который все еще находится в package папка, почему она выдает сообщение, beyond top-level package. В чем именно причина этого сообщения об ошибке?

4b9b3361

Ответ 1

Эта же проблема отмечена в этом вопросе с более последовательным ответом: импорт пакета родного брата

Почему это не работает? Это потому, что python не записывает, откуда был загружен пакет. Поэтому, когда вы делаете python -m test_A.test, он просто отбрасывает знание о том, что test_A.test на самом деле хранится в package (то есть package не считается пакетом). Попытка from..A import foo пытается получить доступ к информации, которой у него больше нет (т. from..A import foo загруженного местоположения). Это концептуально аналогично разрешению from..os import path в файле по math. Это было бы плохо, потому что вы хотите, чтобы пакеты были разными. Если им нужно использовать что-то из другого пакета, то они должны обращаться к ним глобально с помощью from os import path и позволить Python работать там, где это происходит с $PATH и $PYTHONPATH.

Когда вы используете python -m package.test_A.test, то использование from..A import foo разрешает просто отлично, потому что он отслеживает, что находится в package и вы просто получаете доступ к дочернему каталогу загруженного местоположения.

Почему Python не считает текущий рабочий каталог пакетом? НЕТ КЛИПА, но, черт возьми, это было бы полезно.

Ответ 2

import sys
sys.path.append("..") # Adds higher directory to python modules path.

Попробуйте это. Работал для меня.

Ответ 3

Предположение:
Если вы находитесь в каталоге package, A и test_A являются отдельными пакетами.

Заключение:
..A Импорт разрешен только в пакете.

Дальнейшие заметки:
Делать относительный импорт доступным только в пакетах полезно, если вы хотите принудительно размещать пакеты по любому пути, расположенному в sys.path.

РЕДАКТИРОВАТЬ:

Я единственный, кто думает, что это безумие !? Почему в мире текущий рабочий каталог не считается пакетом? - Мультихантер

Текущий рабочий каталог обычно находится в sys.path. Итак, все файлы там импортируются. Это поведение начиная с Python 2, когда пакеты еще не существовали. Создание действующего каталога как пакета позволило бы импортировать модули как "import.A" и как "import A", которые затем будут двумя разными модулями. Может быть, это несоответствие, чтобы рассмотреть.

Ответ 4

from package.A import foo

Я думаю, что это яснее, чем

import sys
sys.path.append("..")

Ответ 5

Ни одно из этих решений не работало для меня в 3.6 с такой структурой папок, как:

package1/
    subpackage1/
        module1.py
package2/
    subpackage2/
        module2.py

Моей целью было импортировать из module1 в module2. Что, наконец, сработало для меня, было, как ни странно,

import sys
sys.path.append(".")

Обратите внимание на одну точку, в отличие от упомянутых выше двухточечных решений.


Изменение: следующее помогло прояснить это для меня:

import os
print (os.getcwd())

В моем случае рабочий каталог был (неожиданно) корневым каталогом проекта.

Ответ 6

Если кто-то все еще испытывает некоторые затруднения после уже предоставленных великолепных ответов, подумайте об этом:

https://www.daveoncode.com/2017/03/07/how-to-solve-python-modulenotfound-no-module-named-import-error/

Основная цитата с вышеуказанного сайта:

"То же самое можно указать программно следующим образом:

импорт системы

sys.path.append( '..')

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

Совершенно очевидно, что так и должно быть, думать об этом после факта. Я пытался использовать sys.path.append('..') в своих тестах, но столкнулся с проблемой, опубликованной OP. Добавив определение import и sys.path до других моих импортов, я смог решить эту проблему.

Ответ 7

Как подсказывает самый популярный ответ, в основном это потому, что ваш PYTHONPATH или sys.path включает . но не ваш путь к вашей посылке. И относительный импорт относится к вашему текущему рабочему каталогу, а не к файлу, в который происходит импорт; как ни странно.

Вы можете исправить это, сначала изменив относительный импорт на абсолютный, а затем либо начав с:

PYTHONPATH=/path/to/package python -m test_A.test

ИЛИ форсировать путь Python при вызове таким образом, потому что:

С python -m test_A.test вы выполняете test_A/test.py с __name__ == '__main__' и __file__ == '/absolute/path/to/test_A/test.py'

Это означает, что в test.py вы можете использовать свой абсолютный import полузащищенный в основном случае, а также выполнить одноразовые манипуляции с путями Python:

from os import path
…
def main():
…
if __name__ == '__main__':
    import sys
    sys.path.append(path.join(path.dirname(__file__), '..'))
    from A import foo

    exit(main())

Ответ 8

если у вас есть __init__.py в верхней папке, вы можете инициализировать импорт как import file/path as alias импорта в import file/path as alias в этом файле инициализации. Затем вы можете использовать его в нижних скриптах как:

import alias