Как сделать относительный импорт в Python?

Представьте себе эту структуру каталогов:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

Я кодирую mod1, и мне нужно что-то импортировать из mod2. Как я должен это делать?

Я пробовал from ..sub2 import mod2, но получаю сообщение «Попытка относительного импорта без пакета».

Я погуглил, но нашел только "манипуляции с sys.path". Нет ли чистого пути?


Обновлено: все мои __init__.py в настоящее время пусты

Edit2: я пытаюсь сделать это, потому что sub2 содержит классы, которые являются общими для подпакетов (sub1, subX и т. д.).

Edit3: поведение, которое я ищу, такое же, как описано в PEP 366 (спасибо John B)

Я рекомендую обновить ваш вопрос, чтобы прояснить, что вы описываете проблему, рассматриваемую в PEP 366.

John B 16.09.2008 23:36

Это длинное объяснение, но посмотрите здесь: stackoverflow.com/a/10713254/1267156 Я ответил на очень похожий вопрос. У меня была такая же проблема до прошлой ночи.

Sevvy325 23.05.2012 08:05

Для тех, кто хочет загрузить модуль, расположенный по произвольному пути, см. Это: stackoverflow.com/questions/67631/…

Evgeni Sergeev 08.06.2014 10:29

В связи с этим, Python 3 изменит стандартную обработку импорта на абсолютную по умолчанию; относительный импорт должен быть явно указан.

Ross 31.03.2015 02:28
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
543
4
344 196
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

Ответ принят как подходящий

Кажется, что все хотят сказать вам, что вы должны делать, а не просто отвечать на вопрос.

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

От PEP 328:

Relative imports use a module's __name__ attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to '__main__') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

В Python 2.6 они добавляют возможность ссылаться на модули относительно основного модуля. PEP 366 описывает изменение.

Обновлять: По словам Ника Коглана, рекомендуемой альтернативой является запуск модуля внутри пакета с помощью ключа -m.

Ответ здесь заключается в том, чтобы возиться с sys.path на каждой точке входа в вашу программу. Думаю, это единственный способ сделать это.

Nick Retallack 11.05.2010 08:27

Рекомендуемая альтернатива - запускать модули внутри пакетов с помощью переключателя -m, а не напрямую указывать их имя файла.

ncoghlan 23.02.2011 07:25

Я не понимаю: где здесь ответ? Как можно импортировать модули в такую ​​структуру каталогов?

Tom 29.09.2012 20:34

@Tom: В этом случае mod1 будет from sub2 import mod2. Затем, чтобы запустить mod1 из приложения, выполните python -m sub1.mod1.

Xiong Chiamiov 20.11.2012 10:06

@XiongChiamiov: означает ли это, что вы не можете этого сделать, если ваш питон встроен в приложение, поэтому у вас нет доступа к переключателям командной строки python?

LarsH 29.01.2013 20:58

@MattJoiner: он работает, если вы запускаете mod1.py как python -m app.sub1.mod1 (из родительского каталога app) как Панкадж написал.

Alexey Kuzminich 25.04.2013 09:12

Чтобы было понятно, когда вы выполняете from .m import whatever в сценарии, который вы передаете в качестве аргумента интерпретатору, это в основном решает для from __main__.m import whatever. Что заставляет искать __main__/m.py.

x-yuri 29.12.2017 17:08

«Кажется, каждый хочет сказать вам, что вы должны делать, а не просто отвечать на вопрос». Что ж, хороший. Вот так получится помощь в написании софта должен быть! Когда я летаю вопреки хорошей практике, Я хочу знать.

jpmc26 03.04.2019 06:52

main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Вы запускаете python main.py.
  2. main.py делает: import app.package_a.module_a
  3. 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 в папки сценариев целевой системы.

Отличный ответ. Есть ли способ импортировать таким образом без установки пакета в PYTHONPATH?

auraham 27.07.2012 20:34

Рекомендуемая дополнительная литература: blog.habnab.it/blog/2013/07/21/python-packages-and-you

nosklo 17.10.2013 15:40

затем, однажды, нужно будет изменить имя приложения на test_app. что случилось бы? Вам нужно будет изменить все исходные коды, импортировать app.package_b.module_b -> test_app.package_b.module_b. это абсолютно ПЛОХАЯ практика ... И мы должны попытаться использовать относительный импорт внутри пакета.

Spybdai 16.12.2016 14:30

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

Я использую этот фрагмент для импорта модулей из путей, надеюсь, что это поможет

Я использую этот фрагмент в сочетании с модулем imp (как описано здесь [1]), чтобы добиться большого эффекта. [1]: stackoverflow.com/questions/1096216/…

Xiong Chiamiov 17.07.2009 01:20

Вероятно, sys.path.append (путь) следует заменить на sys.path.insert (0, путь), а sys.path [-1] следует заменить на sys.path [0]. В противном случае функция импортирует неправильный модуль, если в пути поиска уже есть модуль с таким же именем. Например, если в текущем каталоге есть some.py, import_path («/ imports / some.py») импортирует неправильный файл.

Alex Che 16.06.2010 12:24

Я согласен! Иногда прецедент будет иметь другой относительный импорт. Используйте sys.path.insert

iElectric 19.06.2010 11:13

Как бы вы воспроизвели поведение from x import y (или *)?

levesque 07.12.2010 22:26

Непонятно, укажите полное использование этого скрипта для решения проблемы OP.

mrgloom 24.09.2018 16:12

От Документ Python,

In Python 2.5, you can switch import‘s behaviour to absolute imports using a from __future__ import absolute_import directive. This absolute- import behaviour will become the default in a future version (probably Python 2.7). Once absolute imports are the default, import string will always find the standard library’s version. It’s suggested that users should begin using absolute imports as much as possible, so it’s preferable to begin writing from pkg import string in your code

Взгляните на http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports. Ты мог бы сделать

from .mod1 import stuff

За исключением того, что нельзя выполнять относительный импорт из модуля 'главный', как говорится в ответе Джона Б.

PuercoPop 03.04.2013 22:04

«Гвидо рассматривает запуск скриптов в пакете как антишаблон» (отклонено PEP-3122)

Я потратил так много времени, пытаясь найти решение, читая соответствующие сообщения здесь, в Stack Overflow, и говорил себе: «Должен быть способ получше!». Похоже, нет.

Примечание: уже упомянутый пеп-366 (созданный примерно в то же время, что и pep-3122) предоставляет те же возможности, но использует другую обратно-совместимую реализацию, т.е. если вы хотите запустить модуль внутри пакета как сценарий и, используйте в нем явный относительный импорт, тогда вы может запустить его с помощью переключателя -m: python -m app.sub1.mod1 или вызвать app.sub1.mod1.main() из сценария верхнего уровня (например, сгенерированного из точек входа setuptools, определенных в setup.py).

jfs 29.04.2013 04:14

+1 за использование инструментов настройки и точек входа - это правильный способ настроить скрипты, которые будут запускаться извне в четко определенном месте, в отличие от бесконечного взлома PYTHONPATH

RecencyEffect 11.03.2020 14:51

Не нашел определения слова «бегать». Для меня это не похоже на то, что «бег» - лучшее определение (для паттерна муравей), потому что в конце «интерпретация» будет связывать зависимости, а не фактически «запускать» ее в смысле немедленного выполнения. Ссылка 1 и ссылка 2

Walter 30.12.2020 02:48

К сожалению, это взлом 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, но это не сработало. Что действительно работало, так это sys.path.insert

if __name__ == '__main__':
    sys.path.insert(0, '../..')

Так что вроде взлома, но все заработало! Так что имейте в виду, если вы хотите, чтобы ваше решение было перекрыть другие пути, вам нужно использовать sys.path.insert (0, pathname), чтобы заставить его работать! Это было очень неприятным камнем преткновения для меня, многие люди говорят, что используют функцию "добавить" в sys.path, но это не работает, если у вас уже есть определенный модуль (я нахожу это очень странным поведением)

sys.path.append('../') отлично работает для меня (Python 3.5.2)

Nister 05.11.2016 12:13

Я думаю, что это нормально, поскольку он локализует взлом исполняемого файла и не влияет на другие модули, которые могут зависеть от ваших пакетов.

Tom Russell 01.12.2017 03:54

Я обнаружил, что проще установить переменную среды "PYTHONPATH" в верхнюю папку:

bash$ export PYTHONPATH=/PATH/TO/APP

тогда:

import sub1.func1
#...more import

конечно, PYTHONPATH является «глобальным», но это еще не доставляло мне хлопот.

По сути, virtualenv позволяет вам управлять операторами импорта.

byxor 09.09.2017 04:13

Вдобавок к тому, что сказал Джон Б., похоже, что установка переменной __package__ должна помочь, а не изменять __main__, что может испортить другие вещи. Но, насколько я мог проверить, он работает не совсем так, как должен.

У меня такая же проблема, и ни PEP 328, ни 366 не решают ее полностью, так как оба, к концу дня, нуждаются в том, чтобы голова пакета была включена в sys.path, насколько я мог понять.

Я также должен упомянуть, что я не нашел, как отформатировать строку, которая должна входить в эти переменные. Это "package_head.subfolder.module_name" что ли?

Позвольте мне просто поместить это здесь для моей справки. Я знаю, что это плохой код Python, но мне нужен был сценарий для проекта, над которым я работал, и я хотел поместить его в каталог scripts.

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

Вот решение, которое мне подходит:

Я делаю относительный импорт как 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 действительно не весело.

Лучший ответ IMHO: не только объясняет, почему у OP возникла проблема, но и находит способ ее решения без изменения способа, которым его модули импортируют. В конце концов, относительный импорт OP был в порядке. Причина заключалась в отсутствии доступа к внешним пакетам при прямом запуске в качестве сценария, для решения которой был разработан -m.

MestreLion 07.11.2013 07:40

Также обратите внимание: этот ответ был дан через 5 лет после вопроса. В то время эти функции были недоступны.

JeremyKun 02.04.2014 01:05

Если вы хотите импортировать модуль из того же каталога, вы можете сделать from . import some_module.

Rotareti 17.09.2016 02:47

объяснение ответа nosklo's с примерами

примечание: все файлы __init__.py пусты.

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

приложение / package_a / fun_a.py

def print_a():
    print 'This is a function in dir package_a'

приложение / 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, что вы и хотите. Правильно??

Как говорит @EvgeniSergeev в комментариях к OP, вы можете импортировать код из файла .py в произвольное место с помощью:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Это взято из этот ТАК ответ.

Это решается на 100%:

  • приложение/
    • main.py
  • настройки/
    • local_setings.py

Импортируйте настройки / 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')

Спасибо! все люди заставляли меня запускать мой скрипт по-другому, вместо того, чтобы рассказывать мне, как решить эту проблему в скрипте. Но мне пришлось изменить код, чтобы использовать sys.path.insert(0, "../settings"), а затем from local_settings import *.

Vit Bernatik 04.11.2016 23:22

Вы должны добавить путь модуля к PYTHONPATH:

export PYTHONPATH = "${PYTHONPATH}:/path/to/your/module/"

Это примерно то же самое, что манипулировать sys.path, поскольку sys.path инициализируется из PYTHONPATH.

Joril 25.03.2020 17:50

@Joril Это правильно, но sys.path должен быть жестко закодирован в исходном коде, в отличие от PYTHONPATH, который является переменной среды и может быть экспортирован.

Giorgos Myrianthous 25.03.2020 17:51

Другие вопросы по теме