Aggiunta di codice a __init__.py


85

Sto dando un'occhiata a come funziona il sistema modello in django e ho notato qualcosa che non capisco.

So che crei un __init__.pyfile vuoto per specificare che la directory corrente è un pacchetto. E che puoi impostare qualche variabile in __init__.pymodo che import * funzioni correttamente.

Ma django aggiunge un gruppo di istruzioni from ... import ... e definisce un gruppo di classi in __init__.py. Perché? Questo non fa solo sembrare le cose disordinate? C'è un motivo per cui richiede questo codice __init__.py?


13
Non si tratta davvero di Django, vero? Sì, l'hai visto per la prima volta in Django, ma sembra più una cosa pura di Python - forse il tag Django non è proprio appropriato.
S.Lott

Non vedo alcuna dichiarazione di importazione in __init__.pydjango 1.8. Era per una versione precedente? in caso affermativo quale versione?
Gobi Dasu

Risposte:


72

Tutte le importazioni in __init__.pyvengono rese disponibili quando si importa il pacchetto (directory) che lo contiene.

Esempio:

./dir/__init__.py:

import something

./test.py:

import dir
# can now use dir.something

EDIT: ho dimenticato di menzionare, il codice __init__.pyviene eseguito la prima volta che importi un modulo da quella directory. Quindi è normalmente un buon posto per inserire qualsiasi codice di inizializzazione a livello di pacchetto.

EDIT2: dgrant ha sottolineato una possibile confusione nel mio esempio. In __init__.py import somethingpuò importare qualsiasi modulo, non necessario dal pacchetto. Ad esempio, possiamo sostituirlo con import datetime, quindi nel nostro livello superiore funzioneranno test.pyentrambi questi snippet:

import dir
print dir.datetime.datetime.now()

e

import dir.some_module_in_dir
print dir.datetime.datetime.now()

La linea di fondo è: tutti i nomi assegnati in __init__.py, siano essi moduli, funzioni o classi importati, sono automaticamente disponibili nello spazio dei nomi del pacchetto ogni volta che si importa il pacchetto o un modulo nel pacchetto.


Va bene, grazie. Ma non sono ancora sicuro del motivo per cui sarebbe una buona idea aggiungere classi a __init__.py Non considero davvero il codice di inizializzazione di queste classi (ma forse mi sbaglio).
Erik

Queste sono probabilmente le classi utili ogni volta che lavori con il pacchetto. Ma non voglio speculare, potrebbero esserci molte ragioni per cui sono lì, oggettive o no :)
Alexander Kojevnikov

13
Questo può anche essere per ragioni storiche. Quando si converte un modulo in un pacchetto, module.py in module / __ init__.py tutto il codice esistente può usarlo come prima, ma ora il modulo può avere sottomoduli.
Łukasz

1
I moduli eseguono genitore __init__.pyimplicitamente. Importando i moduli al suo interno __init__.py, si creano importazioni cicliche. Il __init__.pynon verrà eseguito completamente prima di una di queste importazioni. È più sicuro tenere __init__.pyvuoto.
Ivo Danihelka

È importante sottolineare che questo non è niente di specifico per i __init__.pyfile. Se avessi un file con dir/other.pyqualcosa di simile from datetime import datetimepotresti anche chiamare dir.other.datetime.now()o anche from dir.other import datetime.
Carles Sala

37

È solo una preferenza personale e ha a che fare con il layout dei tuoi moduli Python.

Supponiamo che tu abbia un modulo chiamato erikutils. Ci sono due modi in cui può essere un modulo, o hai un file chiamato erikutils.py sul tuo sys.patho hai una directory chiamata erikutils sul tuo sys.pathcon un __init__.pyfile vuoto al suo interno. Quindi diciamo che si dispone di un gruppo di moduli chiamati fileutils, procutils, parseutilse si desidera quelle da sottomoduli sotto erikutils. Quindi crei alcuni file .py chiamati fileutils.py , procutils.py e parseutils.py :

erikutils
  __init__.py
  fileutils.py
  procutils.py
  parseutils.py

Forse avete un paio di funzioni che semplicemente non appartengono nei fileutils, procutilso parseutilsmoduli. E diciamo che non hai voglia di creare un nuovo modulo chiamato miscutils. E, vorresti poter chiamare la funzione in questo modo:

erikutils.foo()
erikutils.bar()

piuttosto che fare

erikutils.miscutils.foo()
erikutils.miscutils.bar()

Quindi, poiché il erikutilsmodulo è una directory, non un file, dobbiamo definire le sue funzioni all'interno del __init__.pyfile.

In Django, il miglior esempio a cui riesco a pensare è django.db.models.fields. TUTTE le classi django * Field sono definite nel __init__.pyfile nella directory django / db / models / fields . Immagino che lo abbiano fatto perché non volevano stipare tutto in un ipotetico modello django / db / models / fields.py , quindi lo hanno diviso in alcuni sottomoduli ( related.py , files.py , per esempio) e hanno bloccato le definizioni * Field fatte nel modulo dei campi stesso (quindi, __init__.py).


1
dgrant, quello che volevo dire è che somethingpuò essere un modulo esterno, dir. qualcosa funzionerà ancora. Grazie per il commento, modificherò il mio post per renderlo più chiaro.
Alexander Kojevnikov,

29

L'utilizzo del __init__.pyfile consente di rendere invisibile dall'esterno la struttura interna del pacchetto. Se la struttura interna cambia (ad esempio perché dividi un modulo fat in due) devi solo modificare il __init__.pyfile, ma non il codice che dipende dal pacchetto. Puoi anche rendere invisibili parti del tuo pacchetto, ad esempio se non sono pronte per l'uso generale.

Nota che puoi usare il delcomando, quindi un tipico __init__.pypotrebbe essere simile a questo:

from somemodule import some_function1, some_function2, SomeObject

del somemodule

Ora se decidi di dividere somemoduleil nuovo __init__.pypotrebbe essere:

from somemodule1 import some_function1, some_function2
from somemodule2 import SomeObject

del somemodule1
del somemodule2

Dall'esterno la confezione sembra ancora esattamente come prima.


1
@Arlen: il punto è che non fa parte dell'API pubblica. Se rinomini un modulo puoi essere certo che nessun codice dipendente si interrompe. Inoltre, ciò garantisce che gli elementi API compaiano solo una volta, ad esempio quando l'introspezione viene utilizzata per creare automaticamente la documentazione API.
Nikow

5
@Arlen: l'eliminazione di un modulo ne impedisce l'accesso import <pack>.somemodule1diretto. È possibile importare solo da <pack>oggetti definiti o importati nei suoi __init__.pysottomoduli e non eliminati.
MestreLion
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.