За ошибкой пакета верхнего уровня в относительном импорте
Кажется, здесь уже есть несколько вопросов об относительном импорте в 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
. В чем именно причина этого сообщения об ошибке?
Ответы
Ответ 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