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

Python: импортировать модуль из другого каталога на том же уровне в иерархии проектов

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

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |
        |------- Scripts/
        |           |
        |           |----- __init__.py
        |           |----- CreateUser.py
        |           |----- FindUser.py

Если я переведу "CreateUser.py" в основной каталог user_management, я могу легко использовать: "import Modules.LDAPManager" для импорта LDAPManager.py - это работает. То, что я не могу сделать (что я хочу сделать), поддерживает CreateUser.py в подпапке Scripts и импортирует LDAPManager.py. Я надеялся выполнить это, используя "import user_management.Modules.LDAPManager.py". Это не работает. Короче говоря, я могу получить файлы Python, чтобы они выглядели глубже в иерархии, но я не могу заставить Python script ссылаться на один каталог и вниз на другой.

Обратите внимание, что я могу решить свою проблему, используя:

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import Modules.LDAPManager as LDAPManager

Я слышал, что это плохая практика и не рекомендуется.

Файлы в сценариях должны выполняться напрямую (требуется ли init.py в скриптах?). Я читал, что в этом случае я должен выполнить CreateUser.py с флагом -m. Я пробовал некоторые варианты этого и просто не могу заставить CreateUser.py распознавать LDAPManager.py.

4b9b3361

Ответ 1

Если я переместил CreateUser.py в основной каталог user_management, я могу легко использовать: import Modules.LDAPManager для импорта LDAPManager.py--- это работает.

Пожалуйста, не. Таким образом, модуль LDAPManager, используемый CreateUser, будет не быть таким же, как тот, который импортируется через другой импорт. Это может создать проблемы, когда у вас есть какое-то глобальное состояние в модуле или во время травления/рассыпания. Избегайте импорта, который работает только потому, что модуль находится в одном каталоге.

Если у вас есть структура пакета, вы должны:

  • Используйте относительный импорт, т.е. если CreateUser.py находится в Scripts/:

     from ..Modules import LDAPManager
    

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

  • Используйте абсолютный импорт, используя полное имя пакета (CreateUser.py в Scripts/):

     from user_management.Modules import LDAPManager
    

Для того, чтобы второй работал, пакет user_management должен быть установлен внутри PYTHONPATH. Во время разработки вы можете настроить среду IDE, чтобы это произошло, без необходимости вручную добавлять вызовы к sys.path.append в любом месте.

Также мне показалось странным, что Scripts/ является подпапкой. Поскольку в реальной установке модуль user_management будет установлен под site-packages, найденным в каталоге lib/ (какой каталог используется для установки библиотек в вашей ОС), в то время как скрипты должны быть установлены в каталоге bin/ (в зависимости от того, что содержит исполняемые файлы для вашей ОС).

На самом деле, я считаю, что Script/ не должно быть даже под user_management. Он должен быть на уровне user_management. Таким образом, вам не нужно использовать -m, но вам просто нужно убедиться, что пакет можно найти (это опять же вопрос настройки IDE, правильной установки пакета или использования PYTHONPATH=. python Scripts/CreateUser.py для запуска скриптов с правильный путь).


Таким образом, я бы использовал следующую иерархию:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |

 Scripts/  (*not* a package)
        |  
        |----- CreateUser.py
        |----- FindUser.py

Тогда код CreateUser.py и FindUser.py должен использовать абсолютный импорт для импорта модулей:

from user_management.Modules import LDAPManager

Во время установки вы убедитесь, что user_management заканчивается где-то в PYTHONPATH и скриптах внутри каталога для исполняемых файлов, чтобы они могли находить модули. Во время разработки вы либо полагаетесь на конфигурацию IDE, либо запускаете CreateUser.py добавление родительского каталога Scripts/ в PYTHONPATH (я имею в виду каталог, который содержит как user_management, так и Scripts):

PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py

Или вы можете изменить PYTHONPATH глобально, так что вам не нужно указывать это каждый раз. В ОС UNIX (Linux, Mac OS X и т.д.) Вы можете изменить один из сценариев оболочки для определения внешней переменной PYTHONPATH, в Windows вам нужно изменить настройки переменных окружения.


Добавление Я считаю, что если вы используете python2, лучше избегать неявного относительного импорта, поставив:

from __future__ import absolute_import

в верхней части ваших модулей. Таким образом, import X всегда означает импортировать модуль верхнего уровня X и никогда не будет пытаться импортировать файл X.py в том же каталоге (если этот каталог не находится в PYTHONPATH). Таким образом, единственный способ сделать относительный импорт - использовать явный синтаксис (from . import X), который лучше (явный лучше, чем неявный).

Это гарантирует, что вам никогда не удастся использовать "фиктивные" неявные относительные импорты, поскольку они повысят четкость ImportError, сигнализируя, что что-то не так. В противном случае вы можете использовать модуль, который не так, как вы думаете.

Ответ 2

Начиная с Python 2.5, вы можете использовать

from ..Modules import LDAPManager

Ведущий период поднимает вас на уровень вашей иерархии.

См. документы Python в ссылках внутри пакета для импорта.

Ответ 3

В "root" __init__.py вы также можете сделать

import sys
sys.path.insert(1, '.')

который должен сделать оба модуля доступными.