Как выполнить относительный импорт в Python?
-
09-06-2019 - |
Вопрос
Представьте себе эту структуру каталогов:
app/
__init__.py
sub1/
__init__.py
mod1.py
sub2/
__init__.py
mod2.py
Я программирую mod1
, и мне нужно что - то импортировать из mod2
.Как я должен это сделать?
Я пытался from ..sub2 import mod2
но я получаю сообщение "Попытка относительного импорта без пакета".
Я погуглил , но нашел только "sys.path
манипулирование" взломами.Разве нет чистого способа?
Редактировать:все мои __init__.py
в настоящее время они пусты
Редактировать 2:Я пытаюсь сделать это, потому что sub2 содержит классы, которые являются общими для всех подпакетов (sub1
, subX
, и т.д.).
Редактировать 3:Поведение, которое я ищу, такое же, как описано в БОДРОСТЬ ДУХА 366 (спасибо, Джон Би)
Решение
Кажется, все хотят сказать вам, что вы должны делать, а не просто ответить на вопрос.
Проблема в том, что вы запускаете модуль как '__main__', передавая mod1.py в качестве аргумента интерпретатору.
Относительный импорт использует атрибут __name__ модуля для определения положения этого модуля в иерархии пакетов.Если имя модуля не содержит никакой информации о пакете (например,он установлен в '__main__'), тогда относительный импорт разрешается так, как если бы модуль был модулем верхнего уровня, независимо от того, где этот модуль фактически расположен в файловой системе.
В Python 2.6 они добавляют возможность ссылаться на модули относительно основного модуля. БОДРОСТЬ ДУХА 366 описывает изменение.
Обновить:По словам Ника Коглана, рекомендуемой альтернативой является запуск модуля внутри пакета с помощью переключателя -m.
Другие советы
main.py
setup.py
app/ ->
__init__.py
package_a/ ->
__init__.py
module_a.py
package_b/ ->
__init__.py
module_b.py
- Ты бежишь
python main.py
. main.py
делает:import app.package_a.module_a
module_a.py
делаетimport app.package_b.module_b
В качестве альтернативы 2 или 3 могли бы использовать: from app.package_a import module_a
Это будет работать до тех пор, пока у вас есть app
в вашем PYTHONPATH. main.py
тогда он мог быть где угодно.
Итак, вы пишете setup.py
скопировать (установить) весь пакет приложения и подпакеты в папки python целевой системы и main.py
к целевым системным папкам скриптов.
Вот решение, которое работает для меня:
Я делаю относительный импорт следующим образом from ..sub2 import mod2
и потом, если я захочу убежать mod1.py
затем я перехожу в родительский каталог app
и запустите модуль, используя переключатель python -m как python -m app.sub1.mod1
.
Реальная причина, по которой эта проблема возникает при относительном импорте, заключается в том, что относительный импорт работает путем использования __name__
свойство модуля.Если модуль запускается непосредственно, то __name__
установлено значение __main__
и он не содержит никакой информации о структуре пакета.И вот почему python жалуется на relative import in non-package
ошибка.
Итак, используя переключатель -m, вы предоставляете python информацию о структуре пакета, с помощью которой он может успешно разрешить относительный импорт.
Я много раз сталкивался с этой проблемой при выполнении относительного импорта.И, прочитав все предыдущие ответы, я все еще не мог понять, как решить эту проблему простым способом, без необходимости помещать шаблонный код во все файлы.(Хотя некоторые комментарии были действительно полезными, спасибо @ncoghlan и @XiongChiamiov)
Надеюсь, это поможет кому-то, кто борется с проблемой относительного импорта, потому что проходить PEP на самом деле не весело.
"Guido рассматривает запущенные скрипты внутри пакета как антишаблон" (отклонено ПЭП-3122)
Я потратил так много времени, пытаясь найти решение, читая связанные сообщения здесь, о переполнении стека, и говоря себе: "должен быть способ получше!".Похоже, что это не так.
Это решаемо на 100%:
- приложение/
- main.py
- Настройки/
- local_setings.py
Импорт settings/local_setting.py в app/main.py:
main.py:
import sys
sys.path.insert(0, "../settings")
try:
from local_settings import *
except ImportError:
print('No Import')
def import_path(fullpath):
"""
Import a file with full path specification. Allows one to
import from anywhere, something __import__ does not do.
"""
path, filename = os.path.split(fullpath)
filename, ext = os.path.splitext(filename)
sys.path.append(path)
module = __import__(filename)
reload(module) # Might be out of date
del sys.path[-1]
return module
Я использую этот фрагмент для импорта модулей из путей, надеюсь, это поможет
объяснение nosklo's
отвечайте примерами
примечание:ВСЕ __init__.py
файлы пусты.
main.py
app/ ->
__init__.py
package_a/ ->
__init__.py
fun_a.py
package_b/ ->
__init__.py
fun_b.py
app/package_a/fun_a.py
def print_a():
print 'This is a function in dir package_a'
app/package_b/fun_b.py
from app.package_a.fun_a import print_a
def print_b():
print 'This is a function in dir package_b'
print 'going to call a function in dir package_a'
print '-'*30
print_a()
main.py
from app.package_b import fun_b
fun_b.print_b()
если ты побежишь $ python main.py
он возвращается:
This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
- main.py делает:
from app.package_b import fun_b
- fun_b.py делает
from app.package_a.fun_a import print_a
итак, файл в папке package_b
использованный файл в папке package_a
, это то, чего ты хочешь.Верно??
К сожалению, это взлом sys.path, но он работает довольно хорошо.
Я столкнулся с этой проблемой с другим слоем:У меня уже был модуль с указанным именем, но это был неправильный модуль.
то, что я хотел сделать, было следующим (модуль, с которым я работал, был module3):
mymodule\
__init__.py
mymodule1\
__init__.py
mymodule1_1
mymodule2\
__init__.py
mymodule2_1
import mymodule.mymodule1.mymodule1_1
Обратите внимание, что я уже установил mymodule, но в моей установке у меня нет "mymodule1".
и я бы получил ImportError, потому что он пытался импортировать данные из моих установленных модулей.
Я попытался создать sys.path.append, но это не сработало.То, что действительно сработало, было системный путь.вставить
if __name__ == '__main__':
sys.path.insert(0, '../..')
Так что это своего рода халтура, но все это заработало!Так что имейте в виду, если вы хотите, чтобы ваше решение переопределение других путей затем вам нужно использовать sys.path.insert(0, имя_патья), чтобы заставить его работать!Это было очень неприятным камнем преткновения для меня, многие люди говорят использовать функцию "добавить" к sys.path, но это не работает, если у вас уже есть определенный модуль (я нахожу это очень странным поведением)
Позвольте мне просто привести это здесь для моей собственной справки.Я знаю, что это плохой код на Python, но мне нужен был скрипт для проекта, над которым я работал, и я хотел поместить скрипт в scripts
справочник.
import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
Как говорит @EvgeniSergeev в комментариях к OP, вы можете импортировать код из .py
файл в произвольном расположении с:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Это взято из это ТАКОЙ ответ.
Взгляните на http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports.Вы могли бы сделать
from .mod1 import stuff
От Python док,
В Python 2.5 вы можете переключить поведение import на абсолютный импорт, используя
from __future__ import absolute_import
директива.Это поведение абсолютного импорта станет стандартным в будущей версии (вероятно, Python 2.7).Как только абсолютный импорт будет установлен по умолчанию,import string
вы всегда найдете версию стандартной библиотеки.Предполагается, что пользователи должны начать использовать абсолютный импорт как можно чаще, поэтому предпочтительнее начать писатьfrom pkg import string
в вашем коде
Я обнаружил, что проще установить переменную среды "PYTHONPATH" в верхнюю папку:
bash$ export PYTHONPATH=/PATH/TO/APP
тогда:
import sub1.func1
#...more import
конечно, PYTHONPATH является "глобальным", но у меня это пока не вызвало проблем.
Вдобавок к тому, что сказал Джон Б., похоже, что установка __package__
переменная должна помочь, вместо того чтобы изменять __main__
что может испортить другие вещи.Но, насколько я мог проверить, это работает не совсем так, как должно.
У меня такая же проблема, и ни PEP 328, ни 366 не решают проблему полностью, поскольку обоим, к концу дня, нужно, чтобы заголовок пакета был включен в sys.path
, насколько я мог понять.
Я должен также упомянуть, что я не нашел, как отформатировать строку, которая должна входить в эти переменные.Так ли это "package_head.subfolder.module_name"
или что?
Предположим, вы работаете на верхнем уровне, затем в mod1
использование:
import sub2.mod2
вместо того, чтобы
from ..sub2 import mod2