it-swarm-ru.tech

Как сделать относительный импорт в 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 (спасибо Джон B)

487
Joril

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

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

От ПКП 328 :

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

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

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

306
John B
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 в целевые папки сценариев системы.

116
nosklo

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

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

114
Pankaj

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

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

46
lesnik

Это решено на 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')
27
Роман Арсеньев
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

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

24
iElectric

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

21
suhailvs

К сожалению, это взлом 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 __== '__main__':
    sys.path.insert(0, '../..')

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

11
Garrett Berg

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

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

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

import imp

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

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

8
LondonRob

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

from .mod1 import stuff
4
mossplix

От Python doc ,

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

2
jung rhew

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

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

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

1
Gabriel

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

bash$ export PYTHONPATH=/PATH/TO/APP

затем:

import sub1.func1
#...more import

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

1
Andrew_1510