Python: importuj moduł z innego katalogu na tym samym poziomie w hierarchii projektu

89

Widziałem wiele przykładów i innych podobnych pytań, ale nie mogę znaleźć przykładu, który dokładnie pasuje do mojego scenariusza. Czuję się jak kompletny łobuz, który zadaje to pytanie, ponieważ jest tak wiele podobnych pytań, ale po prostu nie wydaje mi się, aby to działało „poprawnie”. Oto mój projekt:

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

Jeśli przeniosę „CreateUser.py” do głównego katalogu user_management, mogę łatwo użyć: "import Modules.LDAPManager"do importu LDAPManager.py - to działa. To, czego nie mogę zrobić (co chcę zrobić), to zachować plik CreateUser.py w podfolderze Scripts i zaimportować plik LDAPManager.py. Miałem nadzieję, że uda mi się to osiągnąć za pomocą "import user_management.Modules.LDAPManager.py". To nie działa. Krótko mówiąc, mogę sprawić, by pliki Pythona łatwo zaglądały głębiej w hierarchię, ale nie mogę uzyskać skryptu Pythona, aby odwoływał się do jednego katalogu i do innego.

Zauważ, że jestem w stanie rozwiązać swój problem za pomocą:

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

Słyszałem, że to zła praktyka i zniechęcenie.

Pliki w skryptach mają być wykonywane bezpośrednio (czy init .py w skryptach jest w ogóle potrzebny?). Czytałem, że w tym przypadku powinienem uruchamiać CreateUser.py z flagą -m. Wypróbowałem kilka odmian tego i po prostu nie mogę zmusić CreateUser.py do rozpoznania LDAPManager.py.

CptSupermrkt
źródło

Odpowiedzi:

67

Jeśli CreateUser.pyprzejdę do głównego katalogu user_management, mogę z łatwością użyć: import Modules.LDAPManagerdo importu LDAPManager.py - to działa.

Proszę, nie rób tego . W ten sposób LDAPManagermoduł wykorzystywany przez CreateUsersię nie być taki sam jak ten importowany za pośrednictwem innych importu. Może to powodować problemy, gdy w module występuje jakiś stan globalny lub podczas wytrawiania / odbierania. Unikaj importu, który działa tylko dlatego, że moduł znajduje się w tym samym katalogu.

Jeśli masz strukturę pakietu, powinieneś:

  • Użyj importu względnego, tj. Jeśli CreateUser.pyznajduje się w Scripts/:

     from ..Modules import LDAPManager
    

    Zauważ, że to było (uwaga przeszłość napięta) zniechęcony przez PEP 8 tylko dlatego, że stare wersje Pythona nie popiera je bardzo dobrze, ale problem ten został rozwiązany rok temu. Aktualna wersja PEP 8 ma sugerować je jako dopuszczalną alternatywą dla importu bezwzględnych. Właściwie to lubię je w paczkach.

  • Użyj importu bezwzględnego, używając całej nazwy pakietu ( CreateUser.pyin Scripts/):

     from user_management.Modules import LDAPManager
    

Aby drugi zadziałał, pakiet user_managementpowinien być zainstalowany wewnątrz PYTHONPATH. Podczas programowania możesz skonfigurować IDE tak, aby tak się stało, bez konieczności ręcznego dodawania wywołań w sys.path.appenddowolnym miejscu.

Wydaje mi się również dziwne, że Scripts/jest to podpakiet. Ponieważ w prawdziwej instalacji user_managementmoduł zostałby zainstalowany w katalogu site-packagesznalezionym w lib/katalogu (niezależnie od tego, który katalog jest używany do instalowania bibliotek w twoim systemie operacyjnym), podczas gdy skrypty powinny być instalowane w bin/katalogu (którykolwiek zawiera pliki wykonywalne dla twojego systemu operacyjnego).

Właściwie uważam, że Script/nie powinno być nawet poniżej user_management. Powinien być na tym samym poziomie user_management. W ten sposób nie musisz używać -m, ale po prostu musisz upewnić się, że pakiet można znaleźć (to znowu kwestia skonfigurowania IDE, poprawnej instalacji pakietu lub użycia PYTHONPATH=. python Scripts/CreateUser.pydo uruchomienia skryptów z poprawną ścieżką).


Podsumowując, hierarchia I byłoby użyć to:

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

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

Następnie kod CreateUser.pyi FindUser.pypowinien używać importu bezwzględnego do importowania modułów:

from user_management.Modules import LDAPManager

Podczas instalacji upewniasz się, że user_managementkończy się gdzieś w PYTHONPATH, a skrypty w katalogu dla plików wykonywalnych, aby mogły znaleźć moduły. Podczas programowania polegasz na konfiguracji IDE lub uruchamiasz CreateUser.pydodawanie Scripts/katalogu nadrzędnego do PYTHONPATH(mam na myśli katalog, który zawiera oba user_managementi Scripts):

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

Lub możesz zmodyfikować PYTHONPATHglobalnie, aby nie trzeba było tego określać za każdym razem. W systemach uniksowych (linux, Mac OS X itp.) Możesz zmodyfikować jeden ze skryptów powłoki, aby zdefiniować PYTHONPATHzmienną zewnętrzną, w systemie Windows musisz zmienić ustawienia zmiennych środowiskowych.


Uzupełnienie Uważam, że jeśli używasz python2, lepiej unikać niejawnego importu względnego, umieszczając:

from __future__ import absolute_import

u góry Twoich modułów. W ten sposób import X zawsze oznacza importować Toplevel modułu Xi nigdy nie będzie próbował zaimportować X.pyplik, który znajduje się w tym samym katalogu (jeśli ten katalog nie jest w PYTHONPATH). W ten sposób jedynym sposobem wykonania importu względnego jest użycie jawnej składni (the from . import X), która jest lepsza ( jawna jest lepsza niż niejawna ).

Dzięki temu nigdy nie użyjesz „fałszywego”, niejawnego importu względnego, ponieważ wywołałoby to ImportErrorwyraźną sygnalizację, że coś jest nie tak. W przeciwnym razie możesz użyć modułu, który nie jest tym, o czym myślisz.

Bakuriu
źródło
Jeśli używasz importu względnego, powinieneś wykonaćpython -m user_management.Scripts.CreateUser
mononoke
14

Począwszy od Pythona 2.5, możesz używać

from ..Modules import LDAPManager

Okres wiodący przenosi cię na wyższy poziom w twojej hierarchii.

Zobacz dokumentację Pythona dotyczącą odwołań do pakietów dotyczących importu.

jonrsharpe
źródło
3

W "root" __init__.pymożesz również zrobić plik

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

co powinno umożliwić importowanie obu modułów.

rdodev
źródło
0

Napotkałem te same problemy. Aby to rozwiązać, użyłem export PYTHONPATH="$PWD". Jednak w tym przypadku będziesz musiał zmodyfikować import w swoim Scriptskatalogu w zależności od poniższych:

Przypadek 1: Jeśli jesteś w katalogu user_management, scriptspowinieneś użyć tego stylu from Modules import LDAPManagerdo importu modułu.

Przypadek 2: Jeśli nie jesteś na user_managementpoziomie 1 main, scriptspowinieneś użyć tego stylu from user_management.Modules import LDAPManagerdo importowania modułów.

Peter Du
źródło