Python: importeer module uit een andere map op hetzelfde niveau in de projecthiërarchie

Ik heb allerlei voorbeelden en andere soortgelijke vragen gezien, maar ik kan geen voorbeeld vinden dat precies overeenkomt met mijn scenario. Ik voel me een totale idioot om dit te vragen omdat er zoveel soortgelijke vragen zijn, maar ik krijg dit gewoon niet “goed” werkend. Hier is mijn project:

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

Als ik “CreateUser.py” naar de hoofddirectory user_management verplaats, kan ik gemakkelijk: "import Modules.LDAPManager"gebruiken om LDAPManager.py te importeren — dit werkt. Wat ik niet kan doen (wat ik wil doen), is CreateUser.py in de submap Scripts houden en LDAPManager.py importeren. Ik hoopte dit te bereiken door "import user_management.Modules.LDAPManager.py"te gebruiken. Dit werkt niet. Kortom, ik kan Python-bestanden gemakkelijk dieper in de hiërarchie laten kijken, maar ik kan geen Python-script krijgen om naar de ene map en naar de andere te verwijzen.

Merk op dat ik mijn probleem kan oplossen met:

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

Ik heb gehoord dat dit een slechte gewoonte is en wordt afgeraden.

De bestanden in Scripts zijn bedoeld om direct te worden uitgevoerd (is de init.py in Scripts zelfs nodig?). Ik heb gelezen dat ik in dit geval CreateUser.py zou moeten uitvoeren met de vlag -m. Ik heb hier wat variaties op geprobeerd en het lijkt erop dat CreateUser.py LDAPManager.py niet herkent.


Antwoord 1, autoriteit 100%

Als ik CreateUser.pynaar de hoofddirectory user_management verplaats, kan ik
gebruik eenvoudig: import Modules.LDAPManagerom LDAPManager.pyte importeren
— dit werkt.

Alsjeblieft, niet doen. Op deze manier zal de LDAPManager-module die wordt gebruikt door CreateUsernietdezelfde zijn als de module die via andere imports is geïmporteerd. Dit kan problemen opleveren wanneer u een globale toestand in de module heeft of tijdens het beitsen/ontbeitsen. Vermijdimports die alleen werken omdat de module zich in dezelfde directory bevindt.

Als u een pakketstructuur heeft, moet u:

  • Gebruik relatieve import, d.w.z. als de CreateUser.pyin Scripts/staat:

    from ..Modules import LDAPManager
    

    Merk op dat dit werd(let op de verledentijd) ontmoedigd door PEP 8alleen omdat oude versies van python ze niet zo goed ondersteunden, maar dit probleem was jaren geleden opgelost. De huidigeversie van PEP 8 suggereertze als een acceptabel alternatief voor absolute import. Ik vind ze leukin pakketten.

  • Gebruik absolute import met de hele pakketnaam(CreateUser.pyin Scripts/):

    from user_management.Modules import LDAPManager
    

Om de tweede te laten werken, moet het pakket user_managementbinnen het PYTHONPATHworden geïnstalleerd. Tijdens de ontwikkeling kunt u de IDE zo configureren dat dit gebeurt, zonder dat u ergens handmatig aanroepen hoeft toe te voegen aan sys.path.append.

Ik vind het ook vreemd dat Scripts/een subpakket is. Omdat in een echte installatie de module user_managementzou worden geïnstalleerd onder de site-packagesin de directory lib/(welke directory ook wordt gebruikt om bibliotheken in uw besturingssysteem), terwijl de scripts moeten worden geïnstalleerd onder een bin/-directory (afhankelijk van welke uitvoerbare bestanden voor uw besturingssysteem bevatten).

In feite geloof ik dat Script/niet eens onder user_managementzou moeten staan. Het moet zich op hetzelfde niveau van user_managementbevinden.
Op deze manier hoef je niet-mte gebruiken, maar je moet er alleen voor zorgen dat het pakket gevonden kan worden (ook dit is een kwestie van de IDE configureren, installeren het pakket correct of door PYTHONPATH=. python Scripts/CreateUser.pyte gebruiken om de scripts met het juiste pad te starten).


Samengevat is de hiërarchie die ikzou gebruiken:

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

Vervolgens moet de code van CreateUser.pyen FindUser.pyabsolute imports gebruiken om de modules te importeren:

from user_management.Modules import LDAPManager

Tijdens de installatie zorg je ervoor dat user_managementergens in de PYTHONPATHterechtkomt, en de scripts in de directory voor uitvoerbare bestanden zodat ze de modules kunnen vinden. Tijdens de ontwikkeling vertrouw je ofwel op de IDE-configuratie, of je start CreateUser.pyen voegt de bovenliggende directory Scripts/toe aan de PYTHONPATH(ik bedoel de directory dat zowel user_managementals scriptsbevat):

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

Of je kunt het PYTHONPATHglobaal wijzigen, zodat je dit niet elke keer hoeft op te geven. Op unix-besturingssystemen (linux, Mac OS X enz.) kunt u een van de shellscripts wijzigen om de externe variabele PYTHONPATHte definiëren, op Windows moet u de instellingen voor omgevingsvariabelen wijzigen.


AddendumIk geloof dat als je python2 gebruikt, het beter is om ervoor te zorgen dat je impliciete relatieve invoer vermijdt door te zetten:

from __future__ import absolute_import

bovenaan je modules. Op deze manier betekent import Xaltijdhet importeren van de toplevelmodule Xen zal nooit proberen de X.py-bestand dat zich in dezelfde map bevindt (als die map niet in het PYTHONPATHstaat). Op deze manier is de enigemanier om een ​​relatieve import uit te voeren het gebruik van de explicietesyntaxis (de from . import X), wat beter is ( expliciet is beter dan impliciet).

Dit zorgt ervoor dat u nooit de “nep” impliciete relatieve imports gebruikt, aangezien deze een ImportErrorzouden opleveren die duidelijk aangeeft dat er iets mis is. Anders zou je een module kunnen gebruiken die niet is wat je denkt dat het is.


Antwoord 2, autoriteit 20%

Vanaf Python 2.5 kunt u

. gebruiken

from ..Modules import LDAPManager

De leidende periode brengt je een niveau omhoog in je hiërarchie.

Bekijk de Python-documenten op intra-package-referentiesvoor import.


Antwoord 3, autoriteit 7%

In de “root” __init__.pykun je ook een

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

wat beide modules importeerbaar zou moeten maken.


Antwoord 4

Ik had dezelfde problemen. Om dit op te lossen heb ik export PYTHONPATH="$PWD"gebruikt. In dit geval moet u echter de invoer in uw scripts-map aanpassen, afhankelijk van het onderstaande:

Geval 1: Als u zich in de map user_managementbevindt, moeten uw scriptsdeze stijl gebruiken from Modules import LDAPManagerom module te importeren.

Geval 2: Als u zich buiten het user_management1-niveau bevindt, zoals main, moeten uw scriptsdeze stijl gebruiken from user_management.Modules import LDAPManagerom modules te importeren.

Other episodes