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


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

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

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

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

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


правка: все мои __init__.py ' s в настоящее время пуст

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

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

16 443

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)

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

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%:

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

импорт settings/local_setting.py in 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.путь взломать, но он работает довольно хорошо.

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

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

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

Как говорит @EvgeniSergeev в комментариях к ОП, вы можете импортировать код из .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