A cosa serve __init__.py?
-
19-08-2019 - |
Domanda
Cosa significa __init__.py
in una directory sorgente di Python?
Soluzione
Era una parte obbligatoria di un pacchetto ( vecchio, pre -3.3 "pacchetto regolare" , non più recente 3.3+ " ; pacchetto spazio dei nomi " ).
Python definisce due tipi di pacchetti, pacchetti regolari e pacchetti di spazi dei nomi. I pacchetti regolari sono pacchetti tradizionali come esistevano in Python 3.2 e precedenti. Un pacchetto normale viene in genere implementato come una directory contenente un file
__init__.py
. Quando viene importato un pacchetto normale, questo file__init__.py
viene eseguito implicitamente e gli oggetti che definisce sono associati ai nomi nello spazio dei nomi del pacchetto. Il file__init__.py
può contenere lo stesso codice Python che può contenere qualsiasi altro modulo e Python aggiungerà alcuni attributi aggiuntivi al modulo quando viene importato.
Ma basta fare clic sul collegamento, contiene un esempio, ulteriori informazioni e una spiegazione dei pacchetti dello spazio dei nomi, il tipo di pacchetti senza __init__.py
.
Altri suggerimenti
I file denominati __init__.py
sono usati per contrassegnare le directory sul disco come directory dei pacchetti Python.
Se hai i file
mydir/spam/__init__.py
mydir/spam/module.py
e mydir
sono sul tuo percorso, puoi importare il codice in module.py
come
import spam.module
o
from spam import module
Se rimuovi il file __init__.py
, Python non cercherà più i sottomoduli all'interno di quella directory, quindi i tentativi di importare il modulo falliranno.
Il file __init__.py
è generalmente vuoto, ma può essere utilizzato per esportare parti selezionate del pacchetto con un nome più conveniente, mantenere funzioni di convenienza, ecc.
Dato l'esempio sopra, è possibile accedere ai contenuti del modulo init come
import spam
basato su questo
Oltre a etichettare una directory come pacchetto Python e definire __all__
, __init__.py
ti consente di definire qualsiasi variabile a livello di pacchetto. In questo modo è spesso conveniente se un pacchetto definisce qualcosa che verrà importato frequentemente, in modo simile all'API. Questo modello promuove l'adesione al Pythonic "flat è meglio del nidificato" filosofia.
Un esempio
Ecco un esempio di uno dei miei progetti, in cui spesso frequento un sessionmaker
chiamato Session
per interagire con il mio database. Ho scritto un "database" pacchetto con alcuni moduli:
database/
__init__.py
schema.py
insertions.py
queries.py
Il mio __init__.py
contiene il seguente codice:
import os
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
engine = create_engine(os.environ['DATABASE_URL'])
Session = sessionmaker(bind=engine)
Dato che qui definisco Sessione
, posso iniziare una nuova sessione usando la sintassi seguente. Questo codice sarebbe lo stesso eseguito dall'interno o dall'esterno del "database" directory del pacchetto.
from database import Session
session = Session()
Naturalmente, questa è una piccola comodità: l'alternativa sarebbe quella di definire Session
in un nuovo file come " create_session.py " nel mio pacchetto di database e avviare nuove sessioni utilizzando:
from database.create_session import Session
session = Session()
Ulteriori letture
Esiste un thread reddit piuttosto interessante che copre gli usi appropriati di __init__.py
qui:
http://www.reddit.com/r/Python/comments/1bbbwk/whats_your_opinion_on_what_
L'opinione della maggioranza sembra essere che i file __init__.py
dovrebbero essere molto sottili per evitare di violare l'espressione " esplicita è meglio che implicita " filosofia.
Esistono 2 motivi principali per __init__.py
-
Per comodità: gli altri utenti non dovranno conoscere la posizione esatta delle funzioni nella gerarchia dei pacchetti.
your_package/ __init__.py file1.py file2.py ... fileN.py
# in __init__.py from file1 import * from file2 import * ... from fileN import *
# in file1.py def add(): pass
quindi altri possono chiamare add () di
from your_package import add
senza conoscere file1, come
from your_package.file1 import add
-
Se si desidera inizializzare qualcosa; ad esempio, registrazione (che dovrebbe essere messa al livello più alto):
import logging.config logging.config.dictConfig(Your_logging_config)
Il file __init__.py
fa in modo che Python tratti le directory che lo contengono come moduli.
Inoltre, questo è il primo file da caricare in un modulo, quindi è possibile utilizzarlo per eseguire il codice che si desidera eseguire ogni volta che viene caricato un modulo o specificare i sottomoduli da esportare.
A partire da Python 3.3, __init__.py
non è più necessario per definire le directory come pacchetti Python importabili.
Controlla PEP 420: Pacchetti di spazi dei nomi impliciti :
Supporto nativo per le directory dei pacchetti che non richiedono
__init__.py
file marker e possono estendersi automaticamente su più segmenti di percorso (ispirati a vari approcci di terze parti ai pacchetti dello spazio dei nomi, come descritto in PEP 420 )
Ecco il test:
$ mkdir -p /tmp/test_init
$ touch /tmp/test_init/module.py /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
├── module.py
└── __init__.py
$ python3
>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module
$ rm -f /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
└── module.py
$ python3
>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module
riferimenti:
https://docs.python.org/3 /whatsnew/3.3.html#pep-420-implicit-namespace-packages
https://www.python.org/dev/peps/pep-0420/
__init__.py non è richiesto per i pacchetti in Python 3?
In Python la definizione di pacchetto è molto semplice. Come Java, la struttura gerarchica e la struttura delle directory sono le stesse. Ma devi avere __init__.py
in un pacchetto. Spiegherò il file __init__.py
con l'esempio seguente:
package_x/
|-- __init__.py
|-- subPackage_a/
|------ __init__.py
|------ module_m1.py
|-- subPackage_b/
|------ __init__.py
|------ module_n1.py
|------ module_n2.py
|------ module_n3.py
__init__.py
può essere vuoto, purché esista. Indica che la directory deve essere considerata come un pacchetto. Naturalmente, __init__.py
può anche impostare il contenuto appropriato.
Se aggiungiamo una funzione in module_n1:
def function_X():
print "function_X in module_n1"
return
Dopo l'esecuzione:
>>>from package_x.subPackage_b.module_n1 import function_X
>>>function_X()
function_X in module_n1
Quindi abbiamo seguito il pacchetto della gerarchia e chiamato module_n1 la funzione. Possiamo usare __init__.py
in subPackage_b in questo modo:
__all__ = ['module_n2', 'module_n3']
Dopo l'esecuzione:
>>>from package_x.subPackage_b import *
>>>module_n1.function_X()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named module_n1
Quindi usando * l'importazione, il pacchetto del modulo è soggetto al contenuto __init__.py
.
Sebbene Python funzioni senza un file __init__.py
, dovresti comunque includerne uno.
Specifica che un pacchetto deve essere trattato come un modulo, quindi includilo (anche se è vuoto).
Esiste anche un caso in cui è possibile utilizzare effettivamente un file __init__.py
:
Immagina di avere la seguente struttura di file:
main_methods
|- methods.py
E method.py
contenevano questo:
def foo():
return 'foo'
Per utilizzare foo ()
è necessario uno dei seguenti:
from main_methods.methods import foo # Call with foo()
from main_methods import methods # Call with methods.foo()
import main_methods.methods # Call with main_methods.methods.foo()
Forse lì hai bisogno (o vuoi) di mantenere method.py
all'interno di main_methods
(runtime / dipendenze per esempio) ma vuoi solo importare main_methods
.
Se hai cambiato il nome di method.py
in __init__.py
, puoi usare foo ()
semplicemente importando main_methods
:
import main_methods
print(main_methods.foo()) # Prints 'foo'
Funziona perché __init__.py
è trattato come parte del pacchetto.
Alcuni pacchetti Python lo fanno effettivamente. Un esempio è con JSON , dove è in esecuzione import json
sta effettivamente importando __init__.py
dal pacchetto json
( vedi la struttura del file del pacchetto qui ):
Codice sorgente:
Lib/json/__init__.py
__init__.py
tratterà la directory in cui si trova come un modulo caricabile.
Per le persone che preferiscono leggere il codice, inserisco qui il commento dell'alchimista a due bit .
$ find /tmp/mydir/
/tmp/mydir/
/tmp/mydir//spam
/tmp/mydir//spam/__init__.py
/tmp/mydir//spam/module.py
$ cd ~
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
>>> module.myfun(3)
9
>>> exit()
$
$ rm /tmp/mydir/spam/__init__.py*
$
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named spam
>>>
Facilita l'importazione di altri file Python. Quando hai inserito questo file in una directory (dire cose) contenente altri file PY, puoi fare qualcosa come import stuff.other.
root\
stuff\
other.py
morestuff\
another.py
Senza questo __init__.py
all'interno della directory directory, non è possibile importare other.py, perché Python non sa dove si trova il codice sorgente per stuff e non è in grado di riconoscerlo come pacchetto .
Un file __init__.py
semplifica l'importazione. Quando un __init__.py
è presente all'interno di un pacchetto, la funzione a ()
può essere importata dal file b.py
in questo modo:
from b import a
Senza di essa, tuttavia, non è possibile importare direttamente. Devi modificare il percorso di sistema:
import sys
sys.path.insert(0, 'path/to/b.py')
from b import a