Как сделать относительный импорт в Python? - PullRequest
484 голосов
/ 16 сентября 2008

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

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)

Ответы [ 15 ]

306 голосов
/ 16 сентября 2008

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

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

С PEP 328 :

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

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

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

115 голосов
/ 21 января 2009
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 в целевые папки сценария системы.

114 голосов
/ 17 марта 2013

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

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

46 голосов
/ 19 ноября 2011

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

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

27 голосов
/ 11 февраля 2016

Это решено на 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')
24 голосов
/ 05 июля 2009
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

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

21 голосов
/ 08 декабря 2013

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

11 голосов
/ 03 марта 2012

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

10 голосов
/ 07 января 2013

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

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
8 голосов
/ 09 июня 2015

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

import imp

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

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

...