Python: importa il modulo da un'altra directory allo stesso livello nella gerarchia del progetto


89

Ho visto tutti i tipi di esempi e altre domande simili, ma non riesco a trovare un esempio che corrisponda esattamente al mio scenario. Mi sento come uno scagnozzo totale a chiederlo perché ci sono così tante domande simili, ma non riesco a farlo funzionare "correttamente". Ecco il mio progetto:

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

Se sposto "CreateUser.py" nella directory principale user_management, posso facilmente usare: "import Modules.LDAPManager"per importare LDAPManager.py --- funziona. Quello che non posso fare (cosa che voglio fare), è mantenere CreateUser.py nella sottocartella Scripts e importare LDAPManager.py. Speravo di ottenere questo risultato utilizzando "import user_management.Modules.LDAPManager.py". Questo non funziona. In breve, posso far sì che i file Python guardino facilmente più in profondità nella gerarchia, ma non riesco a ottenere uno script Python per fare riferimento su una directory e giù in un'altra.

Nota che sono in grado di risolvere il mio problema utilizzando:

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

Ho sentito che questa è una cattiva pratica e scoraggiata.

I file in Scripts sono pensati per essere eseguiti direttamente (è addirittura necessario init .py in Scripts?). Ho letto che in questo caso dovrei eseguire CreateUser.py con il flag -m. Ho provato alcune variazioni su questo e non riesco a far sì che CreateUser.py riconosca LDAPManager.py.

Risposte:


67

Se mi sposto CreateUser.pynella directory principale user_management, posso facilmente usare: import Modules.LDAPManagerto import LDAPManager.py --- funziona.

Per favore, non farlo . In questo modo il LDAPManagermodulo utilizzato da CreateUsersarà non essere uguale a quello importato tramite altre importazioni. Ciò può creare problemi quando si dispone di uno stato globale nel modulo o durante il decapaggio / rimozione. Evitare importazioni che funzionano solo perché il modulo si trova nella stessa directory.

Quando hai una struttura del pacchetto dovresti:

  • Usa le importazioni relative, ovvero se CreateUser.pyè in Scripts/:

     from ..Modules import LDAPManager
    

    Si noti che questo è stato (notare il passato ) scoraggiato da PEP 8 solo perché le vecchie versioni di python non le supportavano molto bene, ma questo problema è stato risolto anni fa. La corrente versione di PEP 8 non li suggerisce come alternativa accettabile importazioni assoluti. In realtà mi piacciono all'interno dei pacchetti.

  • Usa importazioni assolute utilizzando l'intero nome del pacchetto ( CreateUser.pyin Scripts/):

     from user_management.Modules import LDAPManager
    

Affinché il secondo funzioni il pacchetto user_managementdeve essere installato all'interno di PYTHONPATH. Durante lo sviluppo è possibile configurare l'IDE in modo che ciò avvenga, senza dover aggiungere manualmente chiamate sys.path.appendovunque.

Inoltre trovo strano che Scripts/sia un sottopacchetto. Perché in un'installazione reale il user_managementmodulo verrebbe installato sotto il site-packagestrovato nella lib/directory (qualunque directory viene utilizzata per installare le librerie nel tuo sistema operativo), mentre gli script dovrebbero essere installati in una bin/directory (a seconda di quale contiene eseguibili per il tuo sistema operativo).

In effetti credo Script/che non dovrebbe nemmeno essere sotto user_management. Dovrebbe essere allo stesso livello di user_management. In questo modo non non è necessario utilizzare -m, ma si deve semplicemente assicurarsi che il pacchetto può essere trovato (questo è ancora una volta una questione di configurare l'IDE, l'installazione del pacchetto correttamente o usando PYTHONPATH=. python Scripts/CreateUser.pyper lanciare gli script con il percorso corretto).


In sintesi, la gerarchia che userei è:

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

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

Quindi il codice di CreateUser.pye FindUser.pydovrebbe utilizzare importazioni assolute per importare i moduli:

from user_management.Modules import LDAPManager

Durante l'installazione assicurati che user_managementfinisca da qualche parte in PYTHONPATH, e gli script all'interno della directory per gli eseguibili in modo che siano in grado di trovare i moduli. Durante lo sviluppo ti affidi alla configurazione IDE o lanci CreateUser.pyaggiungendo la Scripts/directory padre a PYTHONPATH(intendo la directory che contiene sia user_managemente Scripts):

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

Oppure puoi modificare PYTHONPATHglobalmente in modo da non doverlo specificare ogni volta. Su sistemi operativi unix (linux, Mac OS X ecc.) Puoi modificare uno degli script della shell per definire la PYTHONPATHvariabile esterna, su Windows devi modificare le impostazioni delle variabili ambientali.


Addendum Credo, se stai usando python2, è meglio assicurarti di evitare importazioni relative implicite inserendo:

from __future__ import absolute_import

nella parte superiore dei tuoi moduli. In questo modo import X sempre significa importare il toplevel modulo Xe non sarà mai provare a importare il X.pyfile è nella stessa directory (se tale directory non è nel PYTHONPATH). In questo modo l' unico modo per eseguire un'importazione relativa è utilizzare la sintassi esplicita (the from . import X), che è migliore ( esplicito è meglio dell'implicito ).

In questo modo non ti capiterà mai di utilizzare le importazioni relative implicite "fasulle", poiché queste genererebbero un ImportErrorchiaro segnale che qualcosa non va. Altrimenti potresti usare un modulo che non è quello che pensi che sia.


Se stai usando importazioni relative, dovresti eseguirepython -m user_management.Scripts.CreateUser
mononoke


3

Nella "root" __init__.pypuoi anche fare un file

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

che dovrebbe rendere entrambi i moduli importabili.


0

Ho affrontato gli stessi problemi. Per risolvere questo problema, ho usato export PYTHONPATH="$PWD". Tuttavia, in questo caso, dovrai modificare le importazioni nella tua Scriptsdirectory in base a quanto segue:

Caso 1: se ti trovi nella user_managementdirectory, scriptsdovresti usare questo stile from Modules import LDAPManagerper importare il modulo.

Caso 2: se non sei al user_managementlivello 1 main, scriptsdovresti usare questo stile from user_management.Modules import LDAPManagerper importare i moduli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.