Относительный импорт в миллиардный раз
Я здесь:
- http://www.python.org/dev/peps/pep-0328/
- http://docs.python.org/2/tutorial/modules.html#packages
- пакеты 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 не возвратится ко мне "покушение на относительный импорт в упаковке"? есть ответ, который включает в себя -м, якобы.
может кто-нибудь, пожалуйста, скажите мне, почему Python дает это сообщение об ошибке,что это означает, не пакет!, почему и как вы определяете "пакет", и точный ответ, поставленный в терминах достаточно легко для детского сада, чтобы поймите.
изменить: импорт был выполнен из консоли.
8 ответов:
скрипт и модуль
вот объяснение. Короткая версия заключается в том, что существует большая разница между прямым запуском файла Python и импортом этого файла из другого места. просто зная, в каком каталоге находится файл, не определяет, в каком пакете Python думает, что он находится. это зависит, кроме того, от того, как вы загружаете файл в Python (путем запуска или импорта).
Есть два способа загрузить Python файл: как сценарий верхнего уровня, или как модуль. Файл загружается как скрипт верхнего уровня, если вы выполняете его напрямую, например, набрав
python myfile.py
в командной строке. Он загружается как модуль, если вы делаетеpython -m myfile
, или если он загружен, когдаimport
оператор встречается внутри некоторого другого файла. Одновременно может быть только один скрипт верхнего уровня; скрипт верхнего уровня-это файл Python, который вы запустили, чтобы начать работу.наименования
когда файл загружается, ему дается имя (которое хранится в его ). Если он был загружен как скрипт верхнего уровня, его имя
__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
заявления внутри него. Если вы попытаетесь сделать это, вы получите "относительно импорта в непакетных" ошибка.Скрипты не могут импортировать relative
то, что вы, вероятно, сделали, это вы пытались бежать
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__
атрибуты, но и . Вот почему я избегаю использования явного символа__name__
для ссылки на "имя" модуля. Поскольку Python 2.6 "имя" модуля эффективно__package__ + '.' + __name__
, или просто__name__
если__package__
иNone
.)
Это действительно проблема в Python. происхождение путаницы заключается в том, что люди ошибочно принимают относительный импорт как относительный путь, которого нет.
например, когда вы пишете в faa.py:
from .. import foo
это имеет смысл только если faa.г был определены и загружены на 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
вот общий рецепт, измененный в соответствии с примером, который я использую прямо сейчас для работы с библиотеками Python, написанными как пакеты, которые содержат взаимозависимые файлы, где я хочу иметь возможность тестировать их части по частям. Давайте назовем это
lib.foo
и сказал, что ему нужен доступ кlib.fileA
для функцииf1
иf2
иlib.fileB
классClass3
.я включил несколько
from __future__ import print_function
линия).этот пример слишком прост, чтобы показать, когда нам нужно вставить в
sys.path
. (См.Ларса ответ для случая, когда мы do это нужно, когда у нас есть два или более уровней каталогов пакетов, а затем мы используемos.path.dirname(os.path.dirname(__file__))
- но это на самом деле не больно здесь.) Это также достаточно безопасно, чтобы сделать это без какpython foo.py
, поведение такое же, как в случае 2.если запустить в пределах как
python -m foo
, поведение аналогично случаям 2 и 3. Однако, путь к не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__'
The предупреждение является новым, но предупрежден-о поведении нет. Это часть того, что некоторые называют двойная ловушка импорта. (Дополнительные сведения см. В разделе вопрос 27487.) Ник Коглен говорит:
обратите внимание, что, хотя мы нарушаем это правило здесь, мы делаем это только когда загружается файл не загружается как часть пакета, и наши модификация специально разработана, чтобы позволить нам получить доступ к другим файлам в этом пакете. (И, как я уже отметил, Мы, вероятно, не должны делать этого вообще для одноуровневых пакетов.) Если бы мы хотели быть очень чистыми, мы могли бы переписать это, например:
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
).
вот одно решение, которое я бы не рекомендовал, но может быть полезно в некоторых ситуациях, когда модули просто не были сгенерированы:
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()
__name__
изменяется в зависимости от того, выполняется ли данный код в глобальном пространстве имен или как часть импортированного модуля.если код не выполняется в глобальном пространстве,
__name__
будет имя модуля. Если он работает в глобальном пространстве имен-например, если вы введете его в консоль или запустите модуль как скрипт с помощьюpython.exe yourscriptnamehere.py
затем__name__
становится"__main__"
.вы увидите много кода python с
if __name__ == '__main__'
используется для проверки, является ли код запускается из глобального пространства имен – это позволяет вам иметь модуль, который удваивается как скрипт.вы пытались сделать эти импорта из консоли?
у меня была аналогичная проблема, когда я не хотел менять Поиск модуля Python путь и необходимо загрузить модуль относительно из скрипта (несмотря на "скрипты не могут импортировать относительный со всеми" Как хорошо объяснил Бренбарн выше).
поэтому я использовал следующий хак. К сожалению, он полагается на
imp
модуль, который стал устаревшим, так как версия 3.4 должна быть отброшена в пользуimportlib
. (Возможно ли это сimportlib
тоже? Я не знать.) Тем не менее, хак работает на данный момент.пример для доступа к членам
moduleX
наsubpackage1
из скрипта, находящегося в :#!/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.путь, используемый для загрузки модулей, как упоминалось Федерико.
#!/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 *
поэтому, придираясь к этому наряду со многими другими, я наткнулся на записку, опубликованную Дориан Б в этой статьи это решило конкретную проблему, с которой я столкнулся, когда я разрабатывал модули и классы для использования с веб-службой, но я также хочу иметь возможность тестировать их, когда я кодирую, используя средства отладчика в PyCharm. Чтобы запустить тесты в автономном классе, я бы включил следующее В конце моего файла класса:
if __name__ == '__main__': # run test code here...
но если я если бы я хотел импортировать другие классы или модули в ту же папку, мне пришлось бы изменить все мои операторы импорта с относительной нотации на локальные ссылки (т. е. удалить точку (.)) Но после прочтения предложения Дориана, я попробовал его "один лайнер", и это сработало! Теперь я могу тестировать в 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 проверяет, запускаем ли мы этот модуль как main или если он используется в другом модуле, который тестируют как main. Возможно, это очевидно, но я предлагаю эту заметку здесь, Если кто-то еще расстроен относительными проблемами импорта выше, может использовать ее.
относительный импорт используйте атрибут имени модуля, чтобы определить его положение в иерархии пакетов. Если имя модуля не содержит никакой информации о пакете (например, он установлен в "main"), то относительный импорт разрешается так, как если бы модуль был модулем верхнего уровня, независимо от того, где модуль фактически расположен в файловой системе.
написал небольшой пакет python для PyPi, который может помочь зрителям этого вопроса. Пакет действует как обходной путь, если вы хотите иметь возможность запускать файлы python, содержащие импорт, содержащий пакеты верхнего уровня из пакета / проекта, не находясь непосредственно в каталоге импортирующего файла. https://pypi.org/project/import-anywhere/