Относительный импорт в миллиардный раз
Я был здесь:
- http://www.python.org/dev/peps/pep-0328/
- http://docs.python.org/2/tutorial/modules.html
- Пакеты Python: относительный импорт
- Пример кода относительного импорта Python не работает
- Окончательный ответ на относительный импорт Python
- Относительный импорт в Python
- Python: отключение относительного импорта
и множество URL-адресов, которые я не копировал, некоторые на SO, некоторые на других сайтах, когда я думал, что быстро найду решение.
Постоянно повторяющийся вопрос заключается в следующем: в Windows 7, 32-битном Python 2.7.3, как мне решить это сообщение "Попытка относительного импорта в не пакет"? Я построил точную копию пакета на pep-0328:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
Я сделал функции с именем спам и яйца в соответствующих модулях. Естественно, это не сработало. Ответ, по-видимому, находится в 4-м URL-адресе, который я перечислил, но это все мои выпускники. Был такой ответ на одном из URL, которые я посетил:
Относительный импорт использует атрибут имени модуля для определения позиции этого модуля в иерархии пакетов. Если имя модуля не содержит никакой информации о пакете (например, для него установлено значение "main"), то относительный импорт разрешается так, как если бы модуль был модулем верхнего уровня, независимо от того, где этот модуль фактически расположен в файловой системе.
Приведенный выше ответ выглядит многообещающе, но для меня это все иероглифы. Итак, мой вопрос, как мне сделать так, чтобы Python не возвращал мне "Попытка относительного импорта в неупакованном виде"? есть ответ, который включает в себя -m, предположительно.
Может кто-нибудь, пожалуйста, скажите мне, почему Python выдает это сообщение об ошибке, что он означает не в пакете! почему и как вы определяете "пакет", и точный ответ, сформулированный в терминах, достаточно простых для понимания детским садом.
Изменить: импорт был сделан из консоли.
18 ответов
Скрипт против модуля
Вот объяснение. Короткая версия заключается в том, что существует большая разница между прямым запуском файла Python и импортом этого файла из другого места. Знание того, в каком каталоге находится файл, не определяет, в каком пакете Python, по его мнению, находится. Это зависит, кроме того, от того, как вы загрузите файл в Python (запустив или импортировав).
Есть два способа загрузки файла Python: как скрипт верхнего уровня или как модуль. Файл загружается как скрипт верхнего уровня, если вы выполняете его напрямую, например, набирая python myfile.py
в командной строке. Он загружается как модуль, если вы делаете python -m myfile
или если он загружен, когда import
оператор встречается внутри другого файла. За один раз может быть только один скрипт верхнего уровня; скрипт верхнего уровня - это файл Python, который вы запустили для начала.
Именование
Когда файл загружен, ему дается имя (которое хранится в его __name__
атрибуты). Если он был загружен как скрипт верхнего уровня, его имя __main__
, Если он был загружен как модуль, его именем является имя файла, которому предшествуют имена любых пакетов / подпакетов, частью которых он является, разделенных точками.
Так, например, в вашем примере:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
если вы импортировали moduleX
(примечание: импортировано, не выполнено напрямую), его имя будет package.subpackage1.moduleX
, Если вы импортировали moduleA
его имя будет package.moduleA
, Однако, если вы запускаете напрямую moduleX
вместо командной строки его имя будет __main__
и если вы запускаете напрямую moduleA
из командной строки его имя будет __main__
, Когда модуль запускается как скрипт верхнего уровня, он теряет свое обычное имя и вместо него __main__
,
Доступ к модулю НЕ через содержащий его пакет
Есть дополнительная складка: имя модуля зависит от того, был ли он импортирован "напрямую" из каталога, в котором он находится, или импортирован через пакет. Это имеет значение только в том случае, если вы запускаете Python в каталоге и пытаетесь импортировать файл в этот же каталог (или его подкаталог). Например, если вы запускаете интерпретатор Python в каталоге package/subpackage1
а затем сделать import moduleX
, имя moduleX
просто будет moduleX
, и не package.subpackage1.moduleX
, Это связано с тем, что Python добавляет текущий каталог к своему пути поиска при запуске; если он находит импортируемый модуль в текущем каталоге, он не будет знать, что этот каталог является частью пакета, и информация о пакете не станет частью имени модуля.
Особый случай, если вы запускаете интерпретатор в интерактивном режиме (например, просто введите python
и начать ввод кода Python на лету). В этом случае название этого интерактивного сеанса __main__
,
Теперь вот что важно для вашего сообщения об ошибке: если имя модуля не имеет точек, оно не считается частью пакета. Неважно, где файл на самом деле находится на диске. Все, что имеет значение, это как его имя, и его имя зависит от того, как вы его загрузили.
Теперь посмотрите на цитату, которую вы включили в свой вопрос:
Относительный импорт использует атрибут имени модуля для определения позиции этого модуля в иерархии пакетов. Если имя модуля не содержит никакой информации о пакете (например, для него установлено значение "main"), то относительный импорт разрешается так, как если бы модуль был модулем верхнего уровня, независимо от того, где этот модуль фактически расположен в файловой системе.
Относительный импорт...
Относительный импорт использует имя модуля, чтобы определить, где он находится в пакете. Когда вы используете относительный импорт, как from .. import foo
точки указывают на увеличение количества уровней в иерархии пакетов. Например, если имя вашего текущего модуля package.subpackage1.moduleX
, затем ..moduleA
будет означать package.moduleA
, Для from .. import
Для работы имя модуля должно содержать как минимум столько же точек, сколько в import
заявление.
... являются относительными только в пакете
Тем не менее, если имя вашего модуля __main__
не считается в упаковке. Его имя не имеет точек, и поэтому вы не можете использовать from .. import
заявления внутри него. Если вы попытаетесь это сделать, вы получите ошибку "относительный импорт в не пакет".
Скрипты не могут импортировать относительные
Что вы, вероятно, сделали, вы пытались бежать moduleX
или тому подобное из командной строки. Когда вы сделали это, его имя было установлено на __main__
, что означает, что относительный импорт в нем не удастся, потому что его имя не показывает, что он находится в пакете. Обратите внимание, что это также произойдет, если вы запустите Python из того же каталога, где находится модуль, а затем попытаетесь импортировать этот модуль, потому что, как описано выше, Python найдет модуль в текущем каталоге "слишком рано", не осознавая, что это часть пакета.
Также помните, что при запуске интерактивного интерпретатора "имя" этого интерактивного сеанса всегда __main__
, Таким образом, вы не можете выполнять относительный импорт напрямую из интерактивного сеанса. Относительный импорт предназначен только для использования в файлах модуля.
Два решения:
Если вы действительно хотите бежать
moduleX
напрямую, но вы все равно хотите, чтобы это считалось частью пакета, вы можете сделатьpython -m package.subpackage1.moduleX
,-m
говорит Python загрузить его как модуль, а не как скрипт верхнего уровня.Или, возможно, вы на самом деле не хотите бежать
moduleX
вы просто хотите запустить какой-нибудь другой скрипт, скажемmyfile.py
, который использует функции внутриmoduleX
, Если это так, положитеmyfile.py
где-то еще --- не внутриpackage
каталог - и запустить его. Если внутриmyfile.py
вы делаете такие вещи, какfrom package.moduleA import spam
, это будет работать нормально.
Заметки
Для любого из этих решений, каталог пакета (
package
в вашем примере) должен быть доступен из пути поиска модуля Python (sys.path
). Если это не так, вы вообще не сможете использовать что-либо в пакете надежно.Начиная с Python 2.6, "имя" модуля в целях разрешения пакетов определяется не только его
__name__
атрибуты, но и__package__
приписывать. Вот почему я избегаю использовать явный символ__name__
чтобы обратиться к "имени" модуля. Начиная с Python 2.6, "имя" модуля эффективно__package__ + '.' + __name__
, или просто__name__
если__package__
являетсяNone
.)
Это действительно проблема в Python. Истоки путаницы в том, что люди ошибочно принимают относительное значение как относительный путь, а это не так.
Например, когда вы пишете в faa.py:
from .. import foo
Это имеет смысл, только если faa.y был идентифицирован и загружен python во время выполнения как часть пакета. В этом случае имя модуля для faa.py будет, например, some_packagename.faa. Если файл был загружен только потому, что он находится в текущем каталоге, при запуске Python его имя не будет ссылаться на какой-либо пакет, и в конечном итоге относительный импорт завершится неудачно.
Простое решение для ссылки на модули в текущем каталоге, это использовать это:
if __package__ is None or __package__ == '':
#uses current directory visibility
import foo
else:
#uses current package visibility
from . import foo
Слишком много длинных ответов на иностранном языке. Так что я постараюсь сделать это кратко.
Если вы напишете
from . import module
противоположно тому, что вы думаете,
module
будет импортирован не из текущего каталога, а из верхнего уровня вашего пакета! Если вы запустите файл .py как сценарий, он просто не знает, где находится верхний уровень, и поэтому откажется работать.
Если вы начнете так
py -m package.module
из каталога выше
package
, тогда python знает, где находится верхний уровень. Это очень похоже на java:
java -cp bin_directory package.class
Так что, позаботившись об этом вместе со многими другими, я наткнулся на заметку, опубликованную Dorian B. в этой статье, в которой решалась конкретная проблема, с которой я столкнулся, когда я разрабатывал модули и классы для использования с веб-сервисом, но я также хочу быть в состоянии проверить их, как я кодирую, используя средства отладчика в PyCharm. Чтобы запустить тесты в отдельном классе, я бы добавил в конец файла моего класса следующее:
if __name__ == '__main__':
# run test code here...
но если бы я хотел импортировать другие классы или модули в той же папке, то мне пришлось бы изменить все мои операторы импорта с относительной нотации на локальные ссылки (т.е. удалить точку (.)) Но после прочтения предложения Дориана я попробовал его ' one-liner'и это сработало! Теперь я могу тестировать в PyCharm и оставлять свой тестовый код на месте, когда использую класс в другом тестируемом классе или когда я использую его в своем веб-сервисе!
# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
from codex import Codex # these are in same folder as module under test!
from dblogger import DbLogger
else:
from .codex import Codex
from .dblogger import DbLogger
Оператор if проверяет, запускаем ли мы этот модуль в качестве основного или он используется в другом модуле, который тестируется в качестве основного. Возможно, это очевидно, но я предлагаю эту заметку здесь на тот случай, если кто-то еще, разочарованный относительными проблемами импорта, может использовать ее.
Вот одно решение, которое я бы не рекомендовал, но может быть полезно в некоторых ситуациях, когда модули просто не генерируются:
import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()
Вот общий рецепт, измененный в качестве примера, который я сейчас использую для работы с библиотеками Python, написанными как пакеты, которые содержат взаимозависимые файлы, и я хочу иметь возможность тестировать их части по частям. Давайте назовем это lib.foo
и сказать, что ему нужен доступ к lib.fileA
для функций f1
а также f2
, а также lib.fileB
для класса Class3
,
Я включил несколько print
звонки, чтобы помочь проиллюстрировать, как это работает. На практике вы хотели бы удалить их (и, возможно, также from __future__ import print_function
линия).
Этот конкретный пример слишком прост, чтобы показать, когда нам действительно нужно вставить запись в sys.path
, (См. Ответ Ларса для случая, когда он нам нужен, когда у нас есть два или более уровня каталогов пакетов, а затем мы используем os.path.dirname(os.path.dirname(__file__))
- но и здесь не очень больно.) Это также достаточно безопасно, чтобы сделать это без if _i in sys.path
тестовое задание. Однако, если каждый импортированный файл вставляет один и тот же путь, например, если оба fileA
а также fileB
хотите импортировать утилиты из пакета - это загромождает sys.path
с одним и тем же путем много раз, так что приятно иметь if _i not in sys.path
в шаблоне.
from __future__ import print_function # only when showing how this works
if __package__:
print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
from .fileA import f1, f2
from .fileB import Class3
else:
print('Not a package; __name__ is {!r}'.format(__name__))
# these next steps should be used only with care and if needed
# (remove the sys.path manipulation for simple cases!)
import os, sys
_i = os.path.dirname(os.path.abspath(__file__))
if _i not in sys.path:
print('inserting {!r} into sys.path'.format(_i))
sys.path.insert(0, _i)
else:
print('{!r} is already in sys.path'.format(_i))
del _i # clean up global name space
from fileA import f1, f2
from fileB import Class3
... all the code as usual ...
if __name__ == '__main__':
import doctest, sys
ret = doctest.testmod()
sys.exit(0 if ret.failed == 0 else 1)
Идея здесь заключается в следующем (и обратите внимание, что все они работают одинаково в python2.7 и python 3.x):
Если запустить как
import lib
или жеfrom lib import foo
как обычный пакетный импорт из обычного кода,__package
являетсяlib
а также__name__
являетсяlib.foo
, Мы берем первый путь кода, импортируя из.fileA
, так далее.Если запустить как
python lib/foo.py
,__package__
будет None и__name__
будет__main__
,Мы берем второй путь кода.
lib
каталог уже будет вsys.path
поэтому нет необходимости добавлять его. Мы импортируем изfileA
, так далее.Если запустить в пределах
lib
каталог какpython foo.py
Поведение такое же, как и в случае 2.Если запустить в пределах
lib
каталог какpython -m foo
поведение аналогично случаям 2 и 3. Однако путь кlib
каталог не находится вsys.path
поэтому мы добавим его перед импортом. То же самое относится, если мы запустим Python, а затемimport foo
,(Поскольку
.
находится вsys.path
нам не нужно добавлять абсолютную версию пути здесь. Это где более глубокая структура вложенности пакета, где мы хотим сделатьfrom ..otherlib.fileC import ...
, имеет значение. Если вы этого не делаете, вы можете опустить всеsys.path
манипулирование целиком.)
Заметки
Все еще есть причуды. Если вы запустите все это снаружи:
$ python2 lib.foo
или же:
$ python3 lib.foo
поведение зависит от содержания lib/__init__.py
, Если это существует и пусто, все хорошо:
Package named 'lib'; __name__ is '__main__'
Но если lib/__init__.py
сам импорт routine
так что он может экспортировать routine.name
прямо как lib.name
, ты получаешь:
$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'
То есть модуль импортируется дважды, один раз через пакет, а затем снова как __main__
так что он работает main
код. Python 3.6 и позже предупреждают об этом:
$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'
Предупреждение новое, а поведение с предупреждением - нет. Это часть того, что некоторые называют ловушкой двойного импорта. (Дополнительные подробности см. В выпуске 27487.) Ник Коглан говорит:
Эта следующая ловушка существует во всех текущих версиях Python, включая 3.3, и может быть кратко изложена в следующем общем руководстве: "Никогда не добавляйте каталог пакета или любой каталог внутри пакета непосредственно в путь Python".
Обратите внимание, что хотя мы нарушаем это правило здесь, мы делаем это только тогда, когда загружаемый файл не загружается как часть пакета, и наша модификация специально разработана, чтобы позволить нам получить доступ к другим файлам в этом пакете. (И, как я отметил, мы, вероятно, не должны делать этого вообще для одноуровневых пакетов.) Если мы хотим быть сверхчистыми, мы могли бы переписать это как, например:
import os, sys
_i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _i not in sys.path:
sys.path.insert(0, _i)
else:
_i = None
from sub.fileA import f1, f2
from sub.fileB import Class3
if _i:
sys.path.remove(_i)
del _i
То есть мы модифицируем sys.path
достаточно долго, чтобы выполнить импорт, а затем вернуть его обратно в прежнее состояние (удалив одну копию _i
если и только если мы добавили одну копию _i
).
Ответ @BrenBarn говорит сам за себя, но если вы похожи на меня, может потребоваться некоторое время, чтобы понять. Вот мой случай и то, как ответ @BrenBarn относится к нему, возможно, это поможет вам.
Дело
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
Используя наш знакомый пример, добавьте к нему, что moduleX.py имеет относительный импорт в..moduleA. Учитывая, что я попытался написать тестовый скрипт в каталоге subpackage1, который импортировал moduleX, но затем получил страшную ошибку, описанную OP.
Решение
Переместите тестовый скрипт на тот же уровень, что и package, и импортируйте package.subpackage1.moduleX
объяснение
Как объяснено, относительный импорт выполняется относительно текущего имени. Когда мой тестовый скрипт импортирует moduleX из той же директории, тогда имя модуля внутри moduleX - это moduleX. Когда он встречает относительный импорт, интерпретатор не может выполнить резервное копирование иерархии пакетов, потому что он уже наверху
Когда я импортирую moduleX сверху, тогда имя внутри moduleX - package.subpackage1.moduleX, и можно найти относительный импорт
У меня была похожая проблема, когда я не хотел менять путь поиска модуля Python, и мне нужно было загрузить модуль относительно скрипта (несмотря на то, что "скрипты не могут импортировать относительно всех", как BrenBarn объяснил выше).
Поэтому я использовал следующий хак. К сожалению, это зависит от imp
модуль, который устарел с версии 3.4, будет удален в пользу importlib
, (Возможно ли это с importlib
, тоже? Я не знаю.) Тем не менее, хак работает на данный момент.
Пример для доступа к членам moduleX
в subpackage1
из сценария, находящегося в subpackage2
папка:
#!/usr/bin/env python3
import inspect
import imp
import os
def get_script_dir(follow_symlinks=True):
"""
Return directory of code defining this very function.
Should work from a module as well as from a script.
"""
script_path = inspect.getabsfile(get_script_dir)
if follow_symlinks:
script_path = os.path.realpath(script_path)
return os.path.dirname(script_path)
# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)
# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST
Более чистый подход, по-видимому, заключается в изменении sys.path, используемого для загрузки модулей, как упомянуто Федерико.
#!/usr/bin/env python3
if __name__ == '__main__' and __package__ is None:
from os import sys, path
# __file__ should be defined in this case
PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *
Вслед за тем, что предложил Ларс, я включил этот подход в экспериментальную новую библиотеку импорта: ultraimport
Это дает программисту больше контроля над импортом и позволяет импортировать на основе файловой системы. Поэтому вы можете делать относительный импорт из скриптов. Родительский пакет не нужен. ultraimports всегда будет работать, независимо от того, как вы запускаете свой код или какой у вас текущий рабочий каталог, потому что ultraimport делает импорт однозначным. Вам не нужно менять sys.path, а также вам не нужен блок try/except, чтобы иногда выполнять относительный импорт, а иногда и абсолютный.
Затем вы должны написать в somefile.py что-то вроде:
import ultraimport
foo = ultraimport('__dir__/foo.py')
__dir__ — это каталог somefile.py, вызывающий функцию ultraimport(). foo.py будет находиться в том же каталоге, что и somefile.py.
Одно предостережение при импорте скриптов, подобных этому, заключается в том, что они содержат дальнейший относительный импорт. В ultraimport есть встроенный препроцессор, который переписывает последующие относительные импорты в ультраимпорты, чтобы они продолжали работать. Тем не менее, в настоящее время это несколько ограничено, поскольку исходный импорт Python неоднозначен, и вы можете сделать с этим очень мало.
Вот очень простое решение на случай, если вы не хотите делать ничего из этого:
- добавить файлы
- бежать с
python -m mymodule
- редактировать
__package__
- добавлять
if
регистрироваться__main__
- редактировать
sys.path
рукой - редактировать
PYTHONPATH
- реструктурировать проект
pip установить importmonkey
Это надежная оболочка для хаков sys.path, позволяющая сделать все простым и аккуратным.
[github] [пип] [документация]
├─ src
│ └─ project
│ └─ mymodule.py
└─ test
└─ test.py
# In test.py
from importmonkey import add_path
add_path("../src/project") # relative to current __file__
import mymodule
# add as many paths as needed, absolute or relative
# unix path conventions work so you can use '..' and '.'
# add_path validates the paths and returns added path as string
Модуль не обязательно должен иметь__init__.py
связанный с ним, чтобы это работало.
Раскрытие принадлежности: Я сделал importmonkey.
__name__
изменяется в зависимости от того, выполняется ли рассматриваемый код в глобальном пространстве имен или как часть импортированного модуля.
Если код не работает в глобальном пространстве, __name__
будет именем модуля. Если он работает в глобальном пространстве имен - например, если вы набираете его в консоли или запускаете модуль как скрипт, используя python.exe yourscriptnamehere.py
затем __name__
становится "__main__"
,
Вы увидите много кода на Python с if __name__ == '__main__'
используется для проверки того, выполняется ли код из глобального пространства имен - это позволяет вам иметь модуль, который дублируется как сценарий.
Вы пытались сделать этот импорт из консоли?
Еще один грязный, но рабочий способ. Предполагается, что вы находитесь на верхнем уровне своего пакета.
import sys
from os.path import dirname, basename
if __package__ is None:
sys.path.append('..')
__package__ = basename(dirname(sys.argv[0]))
from . import your_module
Преимущество по сравнению с другим ответом здесь заключается в том, что вам не нужно изменять импорт, который автоматически генерируется IDE.
Относительный импорт использует атрибут имени модуля для определения позиции этого модуля в иерархии пакетов. Если имя модуля не содержит никакой информации о пакете (например, для него установлено значение "main"), то относительный импорт разрешается так, как если бы модуль был модулем верхнего уровня, независимо от того, где этот модуль фактически расположен в файловой системе.
Написал небольшой пакет Python для PyPi, который может помочь зрителям в этом вопросе. Пакет действует как обходной путь, если кто-то хочет иметь возможность запускать файлы python, содержащие импорт, содержащий пакеты верхнего уровня, из пакета / проекта, не находясь непосредственно в каталоге импортируемого файла. https://pypi.org/project/import-anywhere/
Добавление моего ответа SO здесь на тот же вопрос, поскольку существует ключевое различие в том, как разработчики пакетов должны структурировать свои операторы импорта по сравнению с конечными пользователями.
Для бедных душ вроде меня, которые ничего не могут сделать из этого, вот решение, опубликованное FEMista .
Добавьте промежуточную родительскую папку, которая будет действовать как общая ветвь для обоих братьев и сестер:
package/
__init__.py
SUBPACKAGES/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
Не забудьте добавить папкуSUBPACKAGES
кimport
пути.
По примеру:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
Добавьте следующую строку вверху скрипта.
# For relative imports to work import sys
Теперь в зависимости от того, откуда вы хотите импортировать модуль, добавьте следующую строку и измените точки перед каждым импортом. В нашем примере мы импортируем
moduleA
изmoduleX
.sys.path.append("..") import moduleA
Прошло 8 лет с тех пор, как OP задал этот вопрос, и по прошествии многих лет Python не решил эту проблему в своих обновлениях.
Относительный импорт не работает, потому что при запуске скрипта он думает, что его имя
__main__
и нет
filename.py
.
Чтобы Python не возвращался ко мне "Попытка относительного импорта в не-пакет". пакет /
init.py subpackage1 / init.py moduleX.py moduleY.py subpackage2 / init.py moduleZ.py moduleA.py
Эта ошибка возникает, только если вы применяете относительный импорт к родительскому файлу. Например, родительский файл уже возвращает main после того, как вы введете код "print (name)" в moduleA.py . Так что этот файл уже является основным, он не может в дальнейшем возвращать родительский пакет. относительный импорт требуется в файлах пакетов subpackage1 и subpackage2, которые вы можете использовать ".." для ссылки на родительский каталог или модуль. Но родительский пакет - если уже пакет верхнего уровня, он не может идти дальше выше этого родительского каталога (пакета). Такие файлы, где вы применяете относительный импорт к родителям, могут работать только с приложением абсолютного импорта. Если вы будете использовать ABSOLUTE IMPORT В РОДИТЕЛЬСКОМ ПАКЕТЕ, НЕТ ОШИБКИ, поскольку python знает, кто находится на верхнем уровне пакета, даже если ваш файл находится в подпакетах из-за концепции PYTHON PATH, которая определяет верхний уровень проекта.
В большинстве случаев, когда я вижу
ValueError: attempted relative import beyond top-level package
и выдернуть мои волосы, решение следующее:
Вам нужно подняться на один уровень выше в файловой иерархии!
#dir/package/module1/foo.py
#dir/package/module2/bar.py
from ..module1 import foo
Импорт при запуске интерпретатора приведет к ошибке, несмотря на то, что процесс импорта никогда не выходит за пределы вашего текущего каталога.
Импорт
bar.py
когда интерпретатор запускается, все получится.
Аналогично для модульных тестов:
python3 -m unittest discover --start-directory=.
успешно работает с
dir/
, но не из
dir/package/
.