Относительный импорт в миллиардный раз

Я был здесь:

и множество 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

Импорт производился с консоли.

Я сделал функции с именами спам и яйца в соответствующих модулях. Естественно, не вышло. Ответ, по-видимому, находится в четвертом URL-адресе, который я перечислил, но для меня это все выпускники. На один из посещенных мной URL был такой ответ:

Относительный импорт использует атрибут имени модуля для определения позиции этого модуля в иерархии пакетов. Если имя модуля не содержит никакой информации о пакете (например, для него установлено значение «main»), то относительный импорт разрешается, как если бы модуль был модулем верхнего уровня, независимо от того, где модуль фактически расположен в файловой системе.

Приведенный выше ответ выглядит многообещающим, но для меня это все иероглифы. Итак, мой вопрос, как мне заставить Python не возвращаться ко мне. Попытка относительного импорта без пакета? есть ответ, который предположительно включает -m.

Может кто-нибудь, пожалуйста, скажите мне, почему Python выдает это сообщение об ошибке, что это означает, что он не является пакетом, почему и как вы определяете `` пакет '', и точный ответ, сформулированный в терминах, достаточно простых для понимания детского сада .


person Community    schedule 03.01.2013    source источник
comment
Как вы пытаетесь использовать файлы, которые показываете? Какой код вы используете?   -  person BrenBarn    schedule 03.01.2013
comment
См. python.org/dev/peps/pep-0328. Я использовал формат пакета, который описал в своем посте. Файлы init .py пусты. moduleY.py имеет def spam(): pass, moduleA.py имеет def eggs(): pass. Я попытался выполнить пару команд из .something import something, но они не работали. Опять же, см. Pep-0328.   -  person    schedule 03.01.2013
comment
Смотрите мой ответ. Вы все еще не полностью разъяснили, что делаете, но если вы пытаетесь сделать from .something import something в интерактивном интерпретаторе, это не сработает. Относительный импорт можно использовать только внутри модулей, но не в интерактивном режиме.   -  person BrenBarn    schedule 03.01.2013
comment
Хорошо, спасибо (ниже я ввел дополнительные вопросы относительно __package__ и не могли бы вы предоставить рабочий пример).   -  person    schedule 03.01.2013
comment
Сам факт, что миллиарды людей - ок. 83 136 на этот комментарий - испытывают достаточно трудностей с импортом, чтобы найти ответ на этот вопрос; мы можем только сделать вывод, что импорт python для многих, если не для большинства программистов, нелогичен. Гвидо, возможно, тебе следует принять это и попросить комитет изменить механизм импорта. Как минимум, этот синтаксис должен работать, если x.py и z.py находятся в одном каталоге. А именно, если x.py имеет оператор, из .z import MyZebraClass x должен импортировать z ДАЖЕ, если он выполняется как main! Почему это так сложно?   -  person Steve L    schedule 11.09.2018
comment
После прочтения большей части этой ветки, хотя это и не ответ на вопрос, просто использование абсолютного импорта кажется решением ...   -  person CodeJockey    schedule 20.11.2018
comment
@CodeJockey, вот что PEP 8 говорит: рекомендуется абсолютный импорт, поскольку они обычно более читабельны и, как правило, ведут себя лучше (или, по крайней мере, дают более качественные сообщения об ошибках). realpython.com содержит ускоренный курс по абсолютному и относительному импорту, который заканчивается это предложение: Относительный импорт также не так удобочитаем, как абсолютный, и нелегко определить местоположение импортированных ресурсов.   -  person Paul Rougieux    schedule 11.05.2020
comment
Связанный вопрос: Python3 правильный способ импорта относительного или абсолютного?   -  person Ani Menon    schedule 03.07.2020
comment
@SteveL дал вам 32 пальца!   -  person cifer    schedule 20.11.2020
comment
Само звание заслуживает +1.   -  person Nae    schedule 27.01.2021
comment
Каждый раз, когда я сталкиваюсь с этой проблемой, мне кажется, что мне нужно прочитать чертову диссертацию, чтобы получить нужные мне примеры кода.   -  person Sledge    schedule 09.02.2021
comment
Я согласен. Это, пожалуй, самая неприятная вещь в Python, и я возвращаюсь к этим темам уже четыре года.   -  person azureai    schedule 17.03.2021
comment
Однажды, когда я окончательно откажусь от Python, это будет названо главной причиной, по которой я оставил язык.   -  person Joshua Schlichting    schedule 26.03.2021


Ответы (13)


Скрипт против модуля

Вот объяснение. Коротко говоря, существует большая разница между непосредственным запуском файла Python и его импортом из другого места. Простое знание того, в каком каталоге находится файл, не определяет, в каком пакете он находится. Это зависит, кроме того, от того, как вы загружаете файл в Python (путем запуска или импорта).

Есть два способа загрузить файл Python: как сценарий верхнего уровня или как модуль. Файл загружается как сценарий верхнего уровня, если вы выполняете его напрямую, например, набрав python myfile.py в командной строке. Он загружается как модуль, когда оператор 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__. Таким образом, нельзя выполнять относительный импорт непосредственно из интерактивного сеанса. Относительный импорт предназначен только для использования в файлах модуля.

Два решения:

  1. Если вы действительно хотите запустить moduleX напрямую, но по-прежнему хотите, чтобы он считался частью пакета, вы можете сделать python -m package.subpackage1.moduleX. -m указывает Python загружать его как модуль, а не как сценарий верхнего уровня.

  2. Или, возможно, вы на самом деле не хотите запускать 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.)

person BrenBarn    schedule 03.01.2013
comment
Вы можете объяснить __package__ больше? Также предположим, что init .py в папке 'package' - это сценарий верхнего уровня, из которого я хочу выполнить. Как мне настроить относительный импорт в ModuleX и в init .py, чтобы они работали? Не могли бы вы привести рабочий пример? - person ; 03.01.2013
comment
@Stopforgettingmyaccounts ...: Пример, который у вас есть, работает, если вы используете его правильно. Если вы хотите запустить этот __init__.py, вы можете сделать python -m package.__init__. Однако это, вероятно, плохая идея, поскольку __init__.py не для этого, и большинство пакетов будут вести себя странно или ничего не делать, если вы это сделаете. Если вы укажете __main__.py в package, вы можете выполнить это с помощью python -m package. См. PEP 366 для получения дополнительной информации о __package__ и __main__.py. - person BrenBarn; 03.01.2013
comment
@Stopforgettingmyaccounts ...: Я согласен, что поведение несколько сбивает с толку. Он не изменен в Python 3. Однако он согласуется с общей философией модуля / пакета Python, заключающейся в том, что фактическая структура каталогов менее важна, чем представление Python о ней через sys.path и т. Д. Я сам часто хочу, чтобы система модулей Python были прозрачным отражением дерева каталогов, но это не так. - person BrenBarn; 03.01.2013
comment
Ссылка на PEP 366 спасает жизнь, поскольку включает специальное решение этой проблемы. Однако я все еще не уверен, как это будет настроено, если вы попытаетесь настроить его обычным образом из main ... и похоже, что Python смотрит на init. py, когда он не может найти ссылку (я понял это, набрав ModuleY вместо moduleY). Я думал, что Python должен быть откровенным, а не таким секретным. - person ; 03.01.2013
comment
Я случайно нажал «Enter», затем нажал «Изменить» и полностью перепечатал текст. Вы, должно быть, посещали ответ между изменениями, извините. Пожалуйста, взгляните на это еще раз. Кроме того, как установить package и будет ли он установлен в init .py или как? - person ; 03.01.2013
comment
@Stopforgettingmyaccounts ...: PEP 366 показывает, как это работает. Внутри файла вы можете делать __package__ = 'package.subpackage1' или что-то подобное. Тогда только этот файл будет всегда считаться частью этого пакета, даже если запускаться напрямую. Если у вас есть другие вопросы по __package__, вы можете задать отдельный вопрос, так как здесь мы уходим от проблемы, связанной с вашим исходным вопросом. - person BrenBarn; 03.01.2013
comment
Это должно быть ответом на все вопросы об относительном импорте Python. Это должно быть даже в документации. - person edsioufi; 13.05.2014
comment
Я перевел этот ответ на 中文 здесь. - person laike9m; 16.07.2015
comment
Насколько я понимаю, этот абзац неполный: когда файл загружается, ему дается имя (которое сохраняется в его атрибуте __name__). Если он был загружен как сценарий верхнего уровня, его имя - __main__. Если он был загружен как модуль, добавьте через import module, его имя - имя файла, добавьте минус .py / .pyc, которому предшествуют имена любых пакетов / подпакетов, частью которых он является, разделенные точками. add Если был загружен как модуль с использованием синтаксиса -m package.module, его имя также будет __main__. - person Michael Scott Cuthbert; 29.08.2015
comment
Также поясните: когда вы используете относительный импорт, такой как from .. import foo, точки указывают на увеличение некоторого количества уровней в иерархии пакетов. Например, если имя вашего текущего модуля package.subpackage1.moduleX, то ..moduleA would mean package.moduleA. Чтобы from .. import работал, в имени модуля должно быть не меньше точек, чем в операторе импорта. Добавьте, что каждый модуль на каждом уровне выше необходимо импортировать. Просто сказать, что это не сработает: if __name__ == '__main__': __package__ = 'package.subpackage1'; '__name__' = 'package.subpackage1.moduleA' - person Michael Scott Cuthbert; 29.08.2015
comment
См. python.org/dev/peps/pep-0366 - Примечание. что этого шаблона достаточно, только если пакет верхнего уровня уже доступен через sys.path. Дополнительный код, который управляет sys.path, потребуется для того, чтобы прямое выполнение работало без импорта пакета верхнего уровня. - это меня больше всего беспокоит, поскольку этот дополнительный код на самом деле довольно длинный и не может быть сохранен где-либо в пакете, чтобы его можно было легко запустить. - person Michael Scott Cuthbert; 29.08.2015
comment
Я продолжаю возвращаться к этому посту, несмотря на то, что являюсь ветераном Python. Основное сообщение для меня: либо возиться с sys.path и __package__ (что довольно некрасиво, см. Другие ответы), либо просто создайте основной сценарий main.py в корневом каталоге вашего проекта и поместите все модули для импорта в подкаталоги. main.py затем может получить доступ ко всем модулям напрямую через их имена пакетов (= имена соответствующих папок, в которых они находятся). - person balu; 27.03.2017
comment
Python добавляет текущий каталог в свой путь поиска, что смутило меня, потому что я думал, что это означает, что directorypython вызывается из. Если быть точным, Python добавляет к своему пути поиска каталог , содержащий сценарий верхнего уровня. Более того, если сценарий верхнего уровня является символической ссылкой, текущий каталог целевой символической ссылки - это то, что добавляется к ее пути поиска. - person Gordon Gustafson; 13.02.2018
comment
Как тот, кто разрабатывает код с использованием pycharm и jupyter, а затем иногда использует его с python rq в качестве задачи, я нахожу чрезвычайно неприятным продолжать получать ImportErrors, особенно когда нет веской причины или объяснения, почему относительный импорт не разрешен - просто расплывчатое упоминание о том, что name является main, как будто это все объясняет. Если я скажу from .mylib import Foo, мне все равно, нахожусь ли я в контексте main (script) - я просто хочу, чтобы мой класс импортировался. Я думаю, что полиция PEP должна что-то сделать :-) (хорошо, пламенный костюм, проверьте.) - person Steve L; 17.09.2018
comment
Этот ответ в настоящее время отключен по нескольким важным деталям, касающимся __name__ и sys.path. В частности, с python -m pkg.mod __name__ устанавливается в __main__, а не pkg.mod; относительный импорт разрешается с использованием __package__, а не __name__ в этом кейс. Кроме того, Python добавляет каталог сценария, а не текущий каталог, в sys.path при запуске python path/to/script.py; он добавляет текущий каталог в sys.path при выполнении большинства других способов, включая python -m pkg.mod. - person user2357112 supports Monica; 05.10.2018
comment
Наконец-то поймите, после нескольких часов чтения ... Стоит отметить, что код под if __name__ == '__main__' все равно будет работать при использовании -m. См. Комментарий от @ user2357112 - person adamF; 22.01.2019
comment
Привет. Но разве модуль по-прежнему не загружается как скрипт с python -m? Я понял, что модуль выполняется только тогда, когда он импортирован. Это подразумевается здесь: docs.python.org/3/library/__main__.html - person Learnerer; 19.05.2019
comment
Можете ли вы обновить свои решения, чтобы мы знали, откуда вы запускаете код? (Рабочий каталог) - person variable; 15.10.2019
comment
Большое спасибо. В документации Python следует упомянуть об этом - «относительный импорт не предназначен для сценариев верхнего уровня». Новичку достаточно, чтобы спросить и понять, почему это так, или вообще проигнорировать эту фичу. - person sanjarcode; 16.01.2021
comment
@BrenBarn спасибо за этот потрясающий ответ. Одно утверждение в вашем ответе, которое все еще сбивает меня с толку после того, как я прочитал его несколько раз: для того, чтобы импорт из ... работал, в имени модуля должно быть как минимум столько точек, сколько в операторе импорта. Если __name__ модуля - package.foo и у него from .. import bar, то интерпретатор может сказать, что foo должен находиться в каталоге, а именно в пакете, который находится в одном из каталогов верхнего уровня (то есть sys.path). .. в основном сказать интерпретатору выйти из каталога пакета, переместиться на один уровень вверх и найти панель? Я пробовал, но не работает ... - person hafan96; 14.06.2021

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

Например, когда вы пишете в faa.py:

from .. import foo

Это имеет значение, только если faa.py был идентифицирован и загружен с помощью 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
person Rami Ka.    schedule 25.03.2018
comment
Правильное решение - from __future__ import absolute_import и заставляет пользователя правильно использовать ваш код ... чтобы вы всегда могли делать from . import foo - person Giacomo Alzetta; 16.07.2018

Вот общий рецепт, измененный в качестве примера, который я использую прямо сейчас для работы с библиотеками 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):

  1. Если запустить как import lib или from lib import foo как обычный импорт пакета из обычного кода, __package будет lib, а __name__ будет lib.foo. Берем первый путь кода, импортируем из .fileA и т. Д.

  2. Если запустить как python lib/foo.py, __package__ будет None, а __name__ будет __main__.

    Берем второй путь кода. Каталог lib уже будет в sys.path, поэтому его не нужно добавлять. Импортируем из fileA и др.

  3. Если запустить в каталоге lib как python foo.py, поведение будет таким же, как и в случае 2.

  4. Если запустить в каталоге lib как python -m foo, поведение аналогично случаям 2 и 3. Однако путь к каталогу lib не находится в sys.path, поэтому мы добавляем его перед импортом. То же самое применимо, если мы запустим Python, а затем import foo.

    (Поскольку . находится в sys.path, нам действительно не нужно добавлять здесь абсолютную версию пути. Здесь важна более глубокая структура вложенности пакетов, в которой мы хотим сделать from ..otherlib.fileC import .... Если вы этого не делаете, вы можете полностью опустить все манипуляции sys.path.)

Примечания

Есть еще причуда. Если запустить все это извне:

$ python2 lib.foo

or:

$ 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).

person torek    schedule 06.05.2017

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

person Steve L    schedule 05.10.2018
comment
Это действительно решает проблему. Но это действительно противно. Почему это не поведение по умолчанию ?! - person lo tolmencre; 04.04.2020
comment
У меня аналогичная проблема - инструменты, которые должны быть упакованы в ту же папку, чтобы работать как надстройка к другой более крупной программе. Основная надстройка взаимодействует с более крупной программой и работает только тогда, когда эта более крупная программа запущена. Для тестирования я хочу запустить более мелкие утилиты и позволить им звонить друг другу. Это кошмар. Я начал просто использовать связанные блоки _1 _ / _ 2_ и добавлять туда все возможные способы импорта чего-либо. Это работает, это коротко, но настолько невероятно непифонично, что каждый раз больно. - person Brownbat; 14.10.2020
comment
Это мой точный вариант использования, тестирование / отладка в PyCharm. Решение для пользователей PyCharm - установить один или несколько «исходных корней». Из документации PyCharm PyCharm использует исходные корни в качестве отправной точки для разрешения импорта. - jetbrains.com/help/pycharm/configuring-project-structure.html - person Briford Wylie; 02.01.2021
comment
Вы сказали, что используете Pycharm. Он сам управляет импортом, и вам придется каждый раз вручную копировать их. Не лучшее решение. - person Smit Johnth; 06.01.2021

Вот одно решение, которое я бы не рекомендовал, но может оказаться полезным в некоторых ситуациях, когда модули просто не были сгенерированы:

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()
person Federico    schedule 21.06.2016

Ответ @BrenBarn говорит обо всем, но если вы похожи на меня, может потребоваться время, чтобы понять. Вот мой случай и то, как ответ @BrenBarn применим к нему, возможно, он вам поможет.

Дело

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

Используя наш знакомый пример, добавьте к нему, что moduleX.py имеет относительный импорт в ..moduleA. Учитывая, что я попытался написать тестовый сценарий в каталоге subpackage1, который импортировал moduleX, но затем получил ужасную ошибку, описанную OP.

Решение

Переместите тестовый сценарий на тот же уровень, что и пакет, и импортируйте package.subpackage1.moduleX

Объяснение

Как объяснялось, относительный импорт выполняется относительно текущего имени. Когда мой тестовый скрипт импортирует moduleX из того же каталога, имя модуля внутри moduleX - moduleX. Когда он встречает относительный импорт, интерпретатор не может создать резервную копию иерархии пакетов, потому что он уже находится наверху

Когда я импортирую moduleX сверху, имя внутри moduleX - package.subpackage1.moduleX, и можно найти относительный импорт

person Brad Dre    schedule 08.08.2019
comment
Надеюсь, ты сможешь помочь мне в этом. В следующей ссылке, если вы перейдете к случаю 3, говорится, что решение 1 невозможно. Пожалуйста, проверьте это и дайте мне знать. Это мне очень поможет. chrisyeh96.github.io/2017/08/08/ - person variable; 19.10.2019
comment
@variable в ссылке опечатка, и мне не разрешено редактировать. Посмотрел на случай 3 и не понял, к чему вы клоните. Когда я попробовал этот пример на python 2, не было проблем, из-за которых я подумал, что я что-то пропустил. Возможно, вам стоит опубликовать новый вопрос, но нужно привести более ясный пример. Случай 4 касается того, о чем я говорю в своем ответе здесь: вы не можете перейти в каталог для относительного импорта, ЕСЛИ интерпретатор не запускается в родительском каталоге. - person Brad Dre; 31.10.2019
comment
Спасибо, я имею в виду python 3, и здесь вопрос stackoverflow.com/questions/58577767/ - person variable; 31.10.2019

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

Если код не выполняется в глобальном пространстве, __name__ будет именем модуля. Если он выполняется в глобальном пространстве имен - например, если вы вводите его в консоль или запускаете модуль как сценарий с помощью python.exe yourscriptnamehere.py, тогда __name__ становится "__main__".

Вы увидите, что много кода Python с if __name__ == '__main__' используется для проверки того, запускается ли код из глобального пространства имен, что позволяет вам иметь модуль, который можно использовать как скрипт.

Вы пробовали делать этот импорт из консоли?

person theodox    schedule 03.01.2013
comment
Ах, значит, вы упомянули -m. Это заставляет ваш модуль работать как скрипт - если вы вставите туда if __name__ == '__main__', вы должны увидеть, что это '__main__' из-за -m. Попробуйте просто импортировать свой модуль в другой модуль, чтобы он не был верхним уровнем ... это должно позволить вам выполнить относительный импорт - person theodox; 03.01.2013
comment
Я попытался выполнить этот импорт из консоли, при этом активный файл был правильным модулем. - person ; 03.01.2013
comment
@Stopforgettingmyaccounts ...: Что значит активный файл? - person BrenBarn; 03.01.2013
comment
Я использую Pyscripter. Я был в moduleX.py, когда запускал этот импорт: from .moduleY import spam и from. import ModuleY. - person ; 03.01.2013
comment
Не импортировать .moduleY, за которым следует moduleY.spam ()? - person theodox; 03.01.2013

На иностранном языке слишком много и слишком длинных ответов. Так что я постараюсь сделать это кратко.

Если вы напишете from . import module, что противоположно тому, что вы думаете, module будет импортирован не из текущего каталога, а из верхнего уровня вашего пакета! Если вы запустите файл .py как сценарий, он просто не знает, где находится верхний уровень, и поэтому откажется работать.

Если вы запустите его так py -m package.module из каталога выше package, тогда python знает, где находится верхний уровень. Это очень похоже на java: java -cp bin_directory package.class


Следующий вопрос: как импортировать с текущего уровня?

person Smit Johnth    schedule 06.01.2021
comment
Это в ответе @BrenBarn, но это TL; DR. OP и все, кто ищет ответы, вот и все. Мне потребовалась вечность, чтобы найти это в другом месте. - person Levi Lesches; 11.06.2021

У меня была аналогичная проблема, когда я не хотел изменять путь поиска модуля Python и мне нужно было загрузить модуль относительно из скрипта (несмотря на то, что "скрипты не могут импортировать относительные с all ", как прекрасно объяснил БренБарн выше).

Итак, я использовал следующий прием. К сожалению, он полагается на модуль 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 *
person Lars    schedule 19.07.2016
comment
Это выглядит лучше ... очень плохо, но все же требуется, чтобы вы вставили имя родительского каталога в файл ... возможно, это можно улучшить с помощью importlib. Возможно, importlib можно даже обезьяно исправить, чтобы относительный импорт работал только для простых случаев использования. Я попробую это сделать. - person Andrew Wagner; 14.03.2017
comment
Однако я использую python 2.7.14. Будет ли что-то вроде этого работать? - person ChumbiChubaGo; 10.02.2018
comment
Я только что протестировал оба подхода на python 2.7.10, и у меня они отлично сработали. На самом деле, у вас нет проблемы устаревшего модуля imp в 2.7, так что тем лучше. - person Lars; 14.02.2018

Относительный импорт использует атрибут имени модуля для определения позиции этого модуля в иерархии пакетов. Если имя модуля не содержит никакой информации о пакете (например, для него установлено значение «main»), то относительный импорт разрешается, как если бы модуль был модулем верхнего уровня, независимо от того, где модуль фактически расположен в файловой системе.

Написал небольшой пакет python для PyPi, который может помочь зрителям этого вопроса. Пакет действует как обходной путь, если кто-то хочет иметь возможность запускать файлы python, содержащие импорты, содержащие пакеты верхнего уровня, из пакета / проекта, не находясь непосредственно в каталоге импортируемого файла. https://pypi.org/project/import-anywhere/

person ec2604    schedule 24.02.2018

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

import sys
from os.path import dirname, basename

if __package__ is None:
    sys.path.insert(0, '..')
    __package__ = basename(dirname(sys.argv[0]))

from . import your_module

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

Изменить: Внимание! Ловушка !!

  • Если вы используете sys.path.append('..') вместо sys.path.insert(0, '..'), это приведет к ошибке в этой файловой структуре. В этом случае будет импортировано your_module.py вместо your_module.
    your_module
      your_module.py
  • Вам необходимо иметь __init__.py в вашем каталоге.
person Smit Johnth    schedule 21.11.2020

По примеру:

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.

person TheOnlyAnil    schedule 15.07.2021
comment
Что и как вы тогда используете filename.py? Как выглядит ваш (минимальный) filename.py файл и находится ли он за пределами package? - person 9769953; 25.07.2021

Чтобы Python не возвращался ко мне Попытка относительного импорта не из пакета.

package/
   __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

Эта ошибка возникает только в том случае, если вы применяете относительный импорт к родительскому файлу. Например, родительский файл уже возвращает __main__ после того, как вы закодировали print(__name__) в moduleA.py

Итак, ЭТО файл уже __main__, он не может возвращать родительский пакет в дальнейшем.

В файлах пакетов subpackage1 и subpackage2 требуется относительный импорт.

Вы можете использовать .. для ссылки на родительский каталог или модуль. Но если родительский файл уже является пакетом верхнего уровня, он не может идти дальше этого родительского каталога (пакета). Такие файлы, в которых вы применяете относительный импорт к родителям, могут работать только с приложением абсолютного импорта.

Если вы используете абсолютный импорт в родительском пакете, НЕТ ОШИБКИ, поскольку python знает, кто находится на верхнем уровне пакета, даже если ваш файл находится в подпакетах, из-за концепции $PYTHONPATH, которая определяет верхний уровень проекта.

person Sakshi Jain    schedule 06.01.2019