Подтвердить что ты не робот

Пути импорта - правильный путь?

Я знаю, что есть много похожих или одинаковых вопросов, но я все еще не могу понять/найти правильный способ для работы с модулями. 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 - мне нужно как-то установить пути в нем.

UPDATE: я попытался добавить файл root.pth в Package1 dir с содержимым ... Но это не сработало - я предполагаю, что это предназначено для чего-то еще.

Выводы:

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

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

4/22/07, Бретт Кэннон написал:

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

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

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

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

4b9b3361

Ответ 1

Какова точка входа для вашей программы? Обычно точка входа для программы будет в корне проекта. Поскольку он находится в корне, все модули внутри корня будут импортированы, если в них есть файл __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

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

Ответ 2

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

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

Ответ 3

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