Кажется, здесь уже есть несколько вопросов об относительном импорте в 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
. В чем именно причина этого сообщения об ошибке?
Ответы:
РЕДАКТИРОВАТЬ: Есть лучшие / более последовательные ответы на этот вопрос в других вопросах:
Почему это не работает? Это потому, что python не записывает, откуда был загружен пакет. Таким образом, когда вы это делаете
python -m test_A.test
, он в основном просто отбрасывает знания, которыеtest_A.test
на самом деле хранятсяpackage
(тоpackage
есть не считается пакетом). Попытка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 не считает текущий рабочий каталог пакетом? НЕТ КЛИПА , но черт возьми, это было бы полезно.
источник
-m
флаг и запускается из каталога выше.sys.path
взлома, а использования setuptools , что, на мой взгляд, гораздо интереснее.Попробуй это. Работал на меня.
источник
A/bar.py
существует и уfoo.py
вас естьfrom .bar import X
.Предположение:
если вы находитесь в
package
каталоге,A
иtest_A
отдельные пакеты.Вывод:
..A
импорт разрешен только внутри пакета.Дополнительные примечания:
Сделать относительный импорт доступным только в пакетах полезно, если вы хотите, чтобы пакеты могли быть размещены на любом пути, расположенном на
sys.path
.РЕДАКТИРОВАТЬ:
Текущий рабочий каталог обычно находится в sys.path. Итак, все файлы там импортируются. Это поведение начиная с Python 2, когда пакеты еще не существовали. Создание действующего каталога как пакета позволило бы импортировать модули как «import .A» и как «import A», которые затем будут двумя разными модулями. Может быть, это несоответствие, чтобы рассмотреть.
источник
python -m package.test_A.test
кажется, делает то, что нужно, и я утверждаю, что это должно быть по умолчанию. Итак, вы можете привести пример несоответствия?#include
был бы очень полезен!Ни одно из этих решений не работало для меня в 3.6 с такой структурой папок, как:
Моей целью было импортировать из module1 в module2. Что, наконец, сработало для меня, было, как ни странно,
Обратите внимание на одну точку, в отличие от упомянутых выше двухточечных решений.
Изменить: следующее помогло прояснить это для меня:
В моем случае рабочий каталог был (неожиданно) корневым каталогом проекта.
источник
sys.path.append(".")
работал, потому что вы вызываете его в родительском каталоге, обратите внимание, что.
всегда представляют каталог, в котором вы запускаете команду python.from package.A import foo
Я думаю, что это яснее, чем
источник
sys.path.append("..")
. протестировано на питоне 3,6Как подсказывает самый популярный ответ, в основном это потому, что ваш
PYTHONPATH
илиsys.path
включает,.
но не ваш путь к вашей посылке. И относительный импорт относится к вашему текущему рабочему каталогу, а не к файлу, в который происходит импорт; как ни странно.Вы можете исправить это, сначала изменив относительный импорт на абсолютный, а затем либо начав с:
ИЛИ форсировать путь Python при вызове таким образом, потому что:
С
python -m test_A.test
вы выполняетеtest_A/test.py
с__name__ == '__main__'
и__file__ == '/absolute/path/to/test_A/test.py'
Это означает, что
test.py
вы можете использовать свой абсолютныйimport
полу-защищенный в главном случае, а также выполнить одноразовые манипуляции с путями Python:источник
Изменить: 2020-05-08: Кажется, что сайт, который я цитировал, больше не контролируется человеком, который написал совет, поэтому я удаляю ссылку на сайт. Спасибо, что сообщили мне знать baxx.
Если кто-то все еще немного борется после того, как хорошие ответы уже предоставлены, я нашел совет на веб-сайте, который больше не доступен.
Основная цитата с сайта, который я упомянул:
Совершенно очевидно, что так и должно быть, думать об этом после факта. Я пытался использовать sys.path.append ('..') в своих тестах, но столкнулся с проблемой, опубликованной OP. Добавив определение import и sys.path до других моих импортов, я смог решить эту проблему.
источник
Если у вас есть
__init__.py
в верхней папке, вы можете инициализировать импорт, какimport file/path as alias
в этом файле инициализации. Затем вы можете использовать его в нижних скриптах как:источник
По моему скромному мнению, я так понимаю этот вопрос:
[Случай 1] Когда вы начинаете абсолютный импорт, как
или
или
вы на самом деле установка импорт-якорь , чтобы быть
test_A
, другими словами, пакет верхнего уровняtest_A
. Итак, когда у нас есть test.py dofrom ..A import xxx
, вы убегаете от якоря, а Python не позволяет этого.[Случай 2] Когда вы делаете
или
Ваш якорь становится
package
, такимpackage/test_A/test.py
образомfrom ..A import xxx
, не удаляется якорь (все еще внутриpackage
папки), и Python с радостью принимает это.Коротко:
Кроме того, мы можем использовать полное имя модуля (FQMN) для проверки этой проблемы.
Проверьте FQMN в каждом случае:
test.__name__
=package.test_A.test
test.__name__
=test_A.test
Таким образом, для CASE2, результатом
from .. import xxx
будет новый модуль с FQMN =package.xxx
, что является приемлемым.В то время как для CASE1,
..
изнутриfrom .. import xxx
выпрыгнет из начального узла (якоря)test_A
, и это НЕ разрешено Python.источник
Не уверен в python 2.x, но в python 3.6, если вы пытаетесь запустить весь пакет, вы просто должны использовать
-t
Итак, на структуре, как
Например, можно использовать:
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
И все еще ввозить
my_module.my_class
без крупных драм.источник