Пути импорта - правильный путь? - PullRequest
27 голосов
/ 24 июня 2011

Я знаю, что есть много подобных или одинаковых вопросов, но я все еще не могу понять / найти правильный способ для меня работать с модулями. Python - мой любимый язык, и мне нравится все в нем, кроме работы с импортом: рекурсивный импорт (когда вы пытаетесь сослаться на имя, которого еще нет), пути импорта и т. Д.

Итак, у меня есть такая структура проекта:

my_project/
    package1/
        __init__.py
        module1
        module2
    package2/
        __init__.py
        module1
        module2

Package1 может использоваться автономное устройство, но также ожидается, что оно будет импортировано package2. Что я делаю сейчас, это то, что, например, в package1.module1 я пишу from package1 import module2, то есть используя полный путь к импортированному модулю. Я делаю это потому, что если я использую import module2 - это не будет работать, когда модуль будет импортирован из другого пакета (package2). Я также не могу использовать from . import module2 - это не будет работать при непосредственном запуске module1.

ОК, поэтому для from package1 import module2 в package1.module1 для работы в обоих случаях (при прямом запуске package1.module1 и при импорте из package2) я добавляю эти строки в начале package1.module1:

import os, sys
currDir = os.path.dirname(os.path.realpath(__file__))
rootDir = os.path.abspath(os.path.join(currDir, '..'))
if rootDir not in sys.path: # add parent dir to paths
    sys.path.append(rootDir)

Для меня это работает, но я чувствую, что это не питон. Я делаю что-то не так?

Должен ли я вместо этого всегда запускать package1.module1 из корня проекта? Если это так, это делает его неудобным для запуска из IDE - мне нужно как-то установить пути в нем.

ОБНОВЛЕНИЕ: Я пытался добавить файл root.pth в package1 dir с содержанием ... Но это не сработало - я думаю, оно предназначено для чего-то другого.

Выводы:

  1. Всегда используйте абсолютный импорт: import package1.module1

  2. Добавьте загрузчик в корневую папку, чтобы запустить некоторые модули в виде отдельного скрипта. Это решает запуск скрипта из среды IDE и является питоническим подходом.

22.04.07 Бретт Кэннон написал:

Этот PEP изменяет идиому if __name__ == "__main__": ... на if __name__ == sys.main: ... чтобы у тебя был хотя бы шанс выполнить модуль в пакете, использующем относительный импорт.

Пробежал этот PEP мимо идей питона. Остановил обсуждение там, когда тоже было предложено много новых идей. =) Я перечислил их всех в раздел «Отклоненные идеи», хотя подавляющая поддержка одной выходит вперед, ПКП может перейти к одному из них.

Я -1 на этом и на любом другом предложенном тидлинге __main__ техника. Кажется, что единственный вариант использования - запуск сценариев жить в каталоге модуля, который я всегда видел как антипаттерн. Чтобы заставить меня передумать, ты должен убедить меня, что это не так.

- Гвидо ван Россум

Ответы [ 3 ]

26 голосов
/ 24 июня 2011

Какова точка входа для вашей программы?Обычно точка входа для программы находится в корне проекта.Поскольку он находится в корневом каталоге, все модули в корневом каталоге будут импортируемыми при условии, что в них есть файл __init__.py.

Итак, используя ваш пример:

my_project/
    main.py
    package1/
        __init__.py
        module1
        module2
    package2/
        __init__.py
        module1
        module2

main.py будет точкой входа для вашей программы.Поскольку файл, который выполняется как главный, автоматически помещается в PYTHONPATH, для импорта верхнего уровня доступны как package1, так и package2.

# in main.py
from package1.module1 import *
from package1.module2 import *

# in package1.module1
import module2
from package2.module1 import *

# in package2.module1 import *
import module2
from package1.module1 import *

Обратите внимание, что в приведенном выше примере package1 и package2 зависятдруг на друга.Это никогда не должно иметь место.Но это всего лишь пример возможности импортировать из любого места.

main.py тоже не должно быть чем-то фантастическим.Это может быть очень просто:

# main.py

if __name__ == '__main__':
    from package1.module1 import SomeClass
    SomeClass().start()

Я хочу подчеркнуть, что если модуль должен быть доступен другим модулям, этот модуль должен быть доступен как импорт верхнего уровня.Модуль не должен пытаться поставить себя в качестве импорта верхнего уровня (непосредственно в PYTHONPATH).

За проект должна отвечать ответственность за обеспечение того, чтобы весь импорт мог быть удовлетворен, еслиМодуль включен непосредственно в проект.Есть два способа сделать это.Во-первых, путем создания файла начальной загрузки, такого как main.py, в папке проекта.Другой способ заключается в создании файла, который добавляет все соответствующие пути к PYTHONPATH, который загружается любыми существующими точками входа.

Например:

# setup.py
import sys

def load():
    paths = ['/path1/','/path2/','/path3/']
    for p in path:
        sys.path.insert(0, p)

# entrypoint.py
from setup import load
load()
# continue with program

Главное, что нужно предпринятьЭто означает, что модуль не должен поставить себя на путь.Путь должен определяться автоматически точкой входа в программу или явно определяться сценарием установки, который знает, где находятся все соответствующие модули.

5 голосов
/ 24 июня 2011

Обычно я создаю каждый пакет как устанавливаемый пакет (т. Е. Создаю файл setup.py), а затем устанавливаю их в virtualenv только для этого проекта, используя pip.

Вы даже можете установить использующий pip -e, если они все еще находятся в разработке.

0 голосов
/ 29 июля 2016

Я опоздал на 5 лет .. но просто сделайте export PYTHONPATH=/path1:/path2: (обратите внимание, что ":") - так ваш рабочий каталог (из которого вы запускаете python) будет в пути.

...