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

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

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

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

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

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


Изменить: все мои __init__.py в настоящее время пустые

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

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

Ответ 1

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

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

От PEP 328:

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

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

Обновление. Согласно Nick Coghlan, рекомендуемая альтернатива - запустить модуль внутри пакета с помощью -m.

Ответ 2

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 в целевые папки script.

Ответ 3

Вот решение, которое работает для меня:

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

Ответ 4

"Guido просматривает сценарии в пакете как анти-шаблон" (отклонено PEP-3122)

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

Ответ 5

Это решение 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')

Ответ 6

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

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

Ответ 7

объяснение 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, что вам и нужно. Право??

Ответ 8

Это, к сожалению, взлом 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, но это не работает, если у вас уже есть определенный модуль (я нахожу это очень странным поведением)

Ответ 9

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

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

Ответ 10

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

import imp

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

Это взято из этого SO-ответа.

Ответ 12

Из Python doc,

В Python 2.5 вы можете переключить импорт на абсолютный импорт с помощью директивы from __future__ import absolute_import. Это абсолютное поведение импорта станет дефолтом в будущей версии (возможно, Python 2.7). Когда абсолютный импорт по умолчанию, import string всегда найдет стандартную версию библиотеки. Он предложил пользователям как можно больше использовать абсолютный импорт, поэтому его предпочтительнее начать писать from pkg import string в вашем коде

Ответ 13

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

bash$ export PYTHONPATH=/PATH/TO/APP

то

import sub1.func1
#...more import

конечно, PYTHONPATH является "глобальным", но для меня это еще не вызвало проблем.

Ответ 14

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

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

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

Ответ 16

Предположим, что вы работаете на верхнем уровне, а затем в mod1 используйте:

import sub2.mod2 

вместо

from ..sub2 import mod2

Ответ 17

Зачем вам это нужно? Почему вы просто не импортируете его как

from app.sub2 import mod2

Ответ 18

Я думаю, что вы должны спросить себя:

  • Зачем мне это нужно?
  • Является ли мое разделение пакетов хорошо выполненным?

Я не знаю контекста, почему вы хотите сделать это таким образом. Но для меня более чистый дизайн будет иметь следующую структуру пакетов:

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

Тогда вам нужно только сделать:

from sub12 import mod2

Ответ 19

Не делайте относительного импорта. Они сделают ваш код более хрупким. Если вы сделаете абсолютный импорт, как предположил Матей, вы будете менее уязвимы к изменениям в sys.path и изменениям в локациях файлов.