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

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

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

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

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

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


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

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

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

16 ответов


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

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

с PEP 328:

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

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

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


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


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

я делаю относительный импорт как 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 рассматривает запущенные скрипты в пакете как анти-шаблон "(отклонено PEP-3122)

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


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

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


это решено 100%:

  • app/
    • 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')

объяснение 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

so файл в папку package_b используется файл в папке package_a, что вы и хотите. Правильно??


это, к сожалению, sys.путь hack, но он работает довольно хорошо.

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

то, что я хотел сделать, было следующим (модуль, с которым я работал, был module3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

обратите внимание, что я уже установил mymodule, но в моей установке у меня нет "mymodule1"

и я бы получил ImportError, потому что это было попытка импорта из установленных модулей.

Я попытался сделать sys.путь.добавить, и это не сработало. Что сработало в sys.путь.вставить

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

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


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


Как говорит @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 doc,

в Python 2.5 вы можете переключить поведение импорта на абсолютный импорт с помощью


я обнаружил, что проще установить переменную среды "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

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

  • зачем мне это делать?
  • хорошо ли сделано разделение пакетов?

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

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

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

from sub12 import mod2