Python: імпорт модуля з іншого каталогу на тому ж рівні в ієрархії проекту


87

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

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 у підпапці Сценарії та імпортувати LDAPManager.py. Я сподівався досягти цього за допомогою "import user_management.Modules.LDAPManager.py". Це не працює. Коротше кажучи, я можу змусити файли Python легко заглянути глибше в ієрархію, але я не можу отримати скрипт Python для посилання вгору на один каталог і вниз в інший.

Зверніть увагу, що я можу вирішити свою проблему, використовуючи:

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

Я чув, що це погана практика і не рекомендується.

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

Відповіді:


66

Якщо я перейду CreateUser.pyв основний каталог user_management, я можу легко використовувати: import Modules.LDAPManagerдля імпорту LDAPManager.py --- це працює.

Будь ласка, не роби . Таким чином, LDAPManagerмодуль , який використовується CreateUserбуде НЕ бути таким же , як той , імпортованими з допомогою іншого імпорту. Це може створити проблеми, коли у вас є якийсь глобальний стан у модулі або під час травлення / зняття. Уникайте імпорту, який працює лише тому, що модуль потрапляє в один каталог.

Коли у вас є структура пакета, вам слід:

  • Використання відносного імпорту, тобто якщо CreateUser.pyв Scripts/:

     from ..Modules import LDAPManager
    

    Зверніть увагу , що це було (зверніть увагу на минуле час) збентежений PEP 8 тільки тому , що старі версії пітона не підтримують їх дуже добре, але ця проблема була вирішена років тому. Тока версія PEP 8 робить запропонувати їх в якості прийнятної альтернативи абсолютного імпорту. Вони насправді мені подобаються всередині пакетів.

  • Використовуйте абсолютний імпорт, використовуючи цілу назву пакета ( CreateUser.pyin 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явного сигналу про те, що щось не так. Інакше ви могли б використовувати модуль, який не такий, як ви думаєте.


Якщо ви використовуєте відносний імпорт, вам слід виконатиpython -m user_management.Scripts.CreateUser
mononoke

14

Починаючи з Python 2.5, ви можете використовувати

from ..Modules import LDAPManager

Провідний період піднімає вас на "рівень" у вашій ієрархії.

Див. Документи Python щодо посилань на пакунки для імпорту.


3

У "корені" __init__.pyви також можете зробити a

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

що повинно зробити обидва модулі імпортними.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.