Script vs. Modulo
Ecco una spiegazione La versione breve è che c'è una grande differenza tra l'esecuzione diretta di un file Python e l'importazione di quel file da qualche altra parte. Il solo fatto di sapere in quale directory si trova un file non determina in quale pacchetto Python pensa che si trovi. Ciò dipende, inoltre, da come si carica il file in Python (eseguendolo o importandolo).
Esistono due modi per caricare un file Python: come script di livello superiore o come modulo. Un file viene caricato come script di livello superiore se lo si esegue direttamente, ad esempio digitando python myfile.py
sulla riga di comando. Se lo fai python -m myfile
, viene caricato come modulo o se viene caricato quando import
viene rilevata un'istruzione all'interno di un altro file. Può esserci solo uno script di livello superiore alla volta; lo script di livello superiore è il file Python che hai eseguito per iniziare le cose.
Naming
Quando viene caricato un file, viene assegnato un nome (che è memorizzato nel suo __name__
attributo). Se è stato caricato come script di livello superiore, il suo nome è __main__
. Se è stato caricato come un modulo, il suo nome è il nome del file, preceduto dai nomi di tutti i pacchetti / subpackages di cui fa parte, separati da punti.
Ad esempio, nel tuo esempio:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
se hai importato moduleX
(nota: importato , non eseguito direttamente), il suo nome sarebbe package.subpackage1.moduleX
. Se importato moduleA
, il suo nome sarebbe package.moduleA
. Tuttavia, se si esegue direttamente moduleX
dalla riga di comando, il suo nome sarà invece __main__
e se si esegue direttamente moduleA
dalla riga di comando, il suo nome sarà __main__
. Quando un modulo viene eseguito come script di livello superiore, perde il suo nome normale e il suo nome è invece __main__
.
Accedere a un modulo NON tramite il suo pacchetto contenente
C'è un'ulteriore ruga: il nome del modulo dipende dal fatto che sia stato importato "direttamente" dalla directory in cui si trova o importato tramite un pacchetto. Questo fa la differenza solo se esegui Python in una directory e provi a importare un file in quella stessa directory (o in una sua sottodirectory). Ad esempio, se avvii l'interprete Python nella directory package/subpackage1
e poi lo fai import moduleX
, il nome moduleX
sarà solo moduleX
e non package.subpackage1.moduleX
. Questo perché Python aggiunge la directory corrente al suo percorso di ricerca all'avvio; se trova il modulo da importare nella directory corrente, non saprà che quella directory fa parte di un pacchetto e le informazioni sul pacchetto non diventeranno parte del nome del modulo.
Un caso speciale è se si esegue l'interprete in modo interattivo (ad esempio, basta digitare python
e iniziare a immettere il codice Python al volo). In questo caso il nome di quella sessione interattiva è __main__
.
Ora ecco la cosa cruciale per il tuo messaggio di errore: se il nome di un modulo non ha punti, non è considerato parte di un pacchetto . Non importa dove il file si trova effettivamente sul disco. Tutto ciò che conta è come si chiama e il suo nome dipende da come lo hai caricato.
Ora guarda la citazione che hai incluso nella tua domanda:
Le importazioni relative utilizzano l'attributo name di un modulo per determinare la posizione di quel modulo nella gerarchia dei pacchetti. Se il nome del modulo non contiene alcuna informazione sul pacchetto (ad esempio, è impostato su "principale"), le importazioni relative vengono risolte come se il modulo fosse un modulo di livello superiore, indipendentemente da dove si trova effettivamente il modulo sul file system.
Importazioni relative ...
Le importazioni relative utilizzano il nome del modulo per determinare dove si trova in un pacchetto. Quando si utilizza un'importazione relativa come from .. import foo
, i punti indicano di aumentare un certo numero di livelli nella gerarchia dei pacchetti. Ad esempio, se il nome del tuo modulo attuale è package.subpackage1.moduleX
, allora ..moduleA
significherebbe package.moduleA
. Per from .. import
funzionare, il nome del modulo deve avere almeno tanti punti quanti sono import
nell'istruzione.
... sono relativi solo in un pacchetto
Tuttavia, se il nome del modulo è __main__
, non è considerato in un pacchetto. Il suo nome non ha punti e pertanto non è possibile utilizzare from .. import
istruzioni al suo interno. Se si tenta di farlo, verrà visualizzato l'errore "importazione relativa in non pacchetto".
Gli script non possono importare relativi
Quello che probabilmente hai fatto è stato provare a eseguire moduleX
o simili dalla riga di comando. Quando lo hai fatto, il suo nome è stato impostato su __main__
, il che significa che le importazioni relative al suo interno falliranno, perché il suo nome non rivela che si trova in un pacchetto. Si noti che ciò accadrà anche se si esegue Python dalla stessa directory in cui si trova un modulo, e quindi si tenta di importare quel modulo, perché, come descritto sopra, Python troverà il modulo nella directory corrente "troppo presto" senza accorgersene parte di un pacchetto.
Ricorda inoltre che quando esegui l'interprete interattivo, il "nome" di quella sessione interattiva è sempre __main__
. Pertanto, non è possibile effettuare importazioni relative direttamente da una sessione interattiva . Le importazioni relative possono essere utilizzate solo all'interno dei file del modulo.
Due soluzioni:
Se vuoi davvero eseguire moduleX
direttamente, ma vuoi comunque che sia considerato parte di un pacchetto, puoi farlo python -m package.subpackage1.moduleX
. La -m
dice Python per caricarlo come modulo, non come lo script di livello superiore.
O forse non vuoi davvero eseguire moduleX
, vuoi solo eseguire qualche altro script, diciamo myfile.py
, che utilizza le funzioni all'interno moduleX
. In tal caso, posizionalo myfile.py
altrove , non all'interno della package
directory, ed eseguilo. Se dentro di myfile.py
te fai cose del genere from package.moduleA import spam
, funzionerà bene.
Appunti
Per una di queste soluzioni, la directory del pacchetto ( package
nel tuo esempio) deve essere accessibile dal percorso di ricerca del modulo Python ( sys.path
). In caso contrario, non sarà possibile utilizzare nulla nel pacchetto in modo affidabile.
A partire da Python 2.6, il "nome" del modulo ai fini della risoluzione del pacchetto è determinato non solo dai suoi __name__
attributi ma anche dall'attributo __package__
. Ecco perché sto evitando di usare il simbolo esplicito __name__
per fare riferimento al "nome" del modulo. Da Python 2.6 il "nome" di un modulo è efficace __package__ + '.' + __name__
, o semplicemente __name__
se lo __package__
è None
.)