Относительный импорт за миллиард
Я был здесь:
и множество 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 выдает это сообщение об ошибке, что означает "не пакет", почему и как вы определяете "пакет", и точный ответ, выраженный в терминах, достаточно простых для понимания в детском саду.
Ответы
Ответ 1
Скрипт против модуля
Здесь объяснение. Короткая версия заключается в том, что существует большая разница между прямым запуском файла 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
операторы 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, модуль "name" по сути является __package__ + '.' + __name__
__package__ + '.' + __name__
или просто __name__
если __package__
None
.)
Ответ 2
Это действительно проблема в 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
Ответ 3
Вот общий рецепт, модифицированный в качестве примера, который я сейчас использую для работы с библиотеками 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__
будет Нет, а __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
).
Ответ 4
Так что, позаботившись об этом вместе со многими другими, я наткнулся на заметку, опубликованную Дорианом Б в этой статье, в которой решалась конкретная проблема, с которой я столкнулся, когда я разрабатывал модули и классы для использования с веб-сервисом, но я также хочу в состоянии проверить их, как я кодирую, используя средства отладчика в 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 проверяет, запускаем ли мы этот модуль как основной или используется ли он в другом модуле, который тестируется как основной. Возможно, это очевидно, но я предлагаю эту заметку здесь на тот случай, если кто-то еще, разочарованный относительными проблемами импорта, может использовать ее.
Ответ 5
__name__
изменяется в зависимости от того, выполняется ли этот код в глобальном пространстве имен или как часть импортированного модуля.
Если код не запущен в глобальном пространстве, __name__
будет именем модуля. Если он запущен в глобальном пространстве имен, например, если вы введете его в консоль или запустите модуль как script с помощью python.exe yourscriptnamehere.py
, тогда __name__
станет "__main__"
.
Вы увидите, что много кода python с if __name__ == '__main__'
используется для проверки того, выполняется ли код из глобального пространства имен, что позволяет вам иметь модуль, который удваивается как script.
Вы пытались выполнить эти импортные операции с консоли?
Ответ 6
Вот одно решение, которое я бы не рекомендовал, но может быть полезным в некоторых ситуациях, когда модули просто не сгенерированы:
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()
Ответ 7
У меня была аналогичная проблема, когда я не хотел менять поиск модуля Python
путь и необходимо загрузить модуль относительно от script (несмотря на то, что "скрипты не могут импортировать относительные со всеми", как хорошо объяснил BrenBarn выше).
Итак, я использовал следующий хак. К сожалению, он полагается на модуль imp
, который
стал устаревшим с версии 3.4 для отказа в пользу importlib
.
(Возможно ли это с помощью importlib
тоже? Я не знаю.) Тем не менее, взлом работает пока.
Пример для доступа к элементам moduleX
в subpackage1
из script, находящихся в папке 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, используемого для загрузки модулей, как упомянуто Federico.
#!/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 *
Ответ 8
Относительный импорт использует атрибут имени модуля для определения этой позиции модуля в иерархии пакетов. Если имя модуля не содержит никакой информации о пакете (например, оно установлено в "main" ), то относительный импорт разрешается, как если бы модуль был модулем верхнего уровня, независимо от того, где модуль фактически находится в файловой системе.
Написал небольшой пакет python для PyPi, который может помочь зрителям в этом вопросе. Пакет действует как обходной путь, если вы хотите иметь возможность запускать файлы python, содержащие импорт, содержащий пакеты верхнего уровня из пакета/проекта, не находясь непосредственно в каталоге файлов импорта. https://pypi.org/project/import-anywhere/
Ответ 9
Ответ @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, и можно найти относительный импорт
Ответ 10
Чтобы 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, которая определяет верхний уровень проекта.