Hinzufügen von Code zu __init__.py

85

Ich schaue mir an, wie das Modellsystem in Django funktioniert, und habe etwas bemerkt, das ich nicht verstehe.

Ich weiß, dass Sie eine leere erstellen __init__.py Datei um anzugeben, dass das aktuelle Verzeichnis ein Paket ist. Und dass Sie eine Variable festlegen können, __init__.pydamit der Import * ordnungsgemäß funktioniert.

Aber django fügt eine Reihe von from ... import ... -Anweisungen hinzu und definiert eine Reihe von Klassen in __init__.py. Warum? Lässt das die Dinge nicht einfach chaotisch aussehen? Gibt es einen Grund, für den dieser Code erforderlich ist __init__.py?

Erik
quelle
13
Hier geht es nicht wirklich um Django, oder? Ja, Sie haben es zuerst in Django gesehen, aber dies scheint eher eine reine Python-Sache zu sein - vielleicht ist das Django-Tag nicht wirklich angemessen.
S.Lott
Ich sehe keine Importanweisungen in __init__.pyDjango 1.8. War das für eine ältere Version? wenn ja welche version?
Gobi Dasu

Antworten:

71

Alle Importe in __init__.py werden verfügbar gemacht, wenn Sie das Paket (Verzeichnis) importieren, das es enthält.

Beispiel:

./dir/__init__.py::

import something

./test.py::

import dir
# can now use dir.something

EDIT: vergessen zu erwähnen, den Code in __init__.py wenn Sie zum ersten Mal ein Modul aus diesem Verzeichnis importieren. Daher ist es normalerweise ein guter Ort, um Initialisierungscode auf Paketebene einzufügen.

EDIT2: dgrant hat in meinem Beispiel auf eine mögliche Verwirrung hingewiesen. In __init__.py import somethingkann jedes Modul importieren, das nicht aus dem Paket benötigt wird. Zum Beispiel können wir es durch ersetzen import datetime, dann test.pyfunktionieren in unserer obersten Ebene beide Snippets:

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

und

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

Das Fazit lautet: Alle in zugewiesenen Namen __init__.py, seien es importierte Module, Funktionen oder Klassen, sind automatisch im Paket-Namespace verfügbar, wenn Sie das Paket oder ein Modul in das Paket importieren.

Alexander Kojevnikov
quelle
Okay danke. Aber ich bin mir immer noch nicht sicher, warum es eine gute Idee wäre, Klassen hinzuzufügen. __init__.py Ich betrachte diesen Klasseninitialisierungscode nicht wirklich (aber vielleicht irre ich mich darin).
Erik
Dies sind wahrscheinlich die Klassen, die jedes Mal nützlich sind, wenn Sie mit dem Paket arbeiten. Aber ich möchte nicht spekulieren, es könnte viele Gründe geben, warum sie da sind, objektiv oder nicht :)
Alexander Kojevnikov
13
Dies kann auch historische Gründe haben. Wenn Sie ein Modul in ein Paket konvertieren, kann module.py in module / __ init__.py verwendet werden. Der gesamte vorhandene Code kann es wie zuvor verwenden, aber jetzt kann das Modul Submodule haben.
Łukasz
1
Module werden übergeordnet __init__.pyimplizit ausgeführt. Durch den Import der Module im Inneren __init__.pyerstellen Sie zyklische Importe. Das __init__.pywird vor einem solchen Import nicht vollständig ausgeführt. Es ist sicherer, __init__.pyleer zu bleiben .
Ivo Danihelka
Es ist wichtig zu beachten, dass dies nichts Spezielles für __init__.pyDateien ist. Wenn Sie eine Datei hätten, dir/other.pydie so etwas wie from datetime import datetimehätte, könnten Sie auch anrufen dir.other.datetime.now()oder sogar from dir.other import datetime.
Carles Sala
36

Es ist wirklich nur eine persönliche Präferenz und hat mit dem Layout Ihrer Python-Module zu tun.

Angenommen, Sie haben ein Modul namens erikutils. Es gibt zwei Möglichkeiten, wie es ein Modul sein kann: Entweder haben Sie eine Datei namens erikutils.py auf Ihrem sys.pathoder Sie haben ein Verzeichnis namens erikutils auf Ihrem, sys.pathin dem sich eine leere __init__.pyDatei befindet. Dann lassen Sie uns sagen , dass Sie eine Reihe von Modulen haben genannt fileutils, procutils, parseutilsund Sie möchten die unter Submodule sein erikutils. Sie erstellen also einige .py-Dateien mit den Namen fileutils.py , procutils.py und parseutils.py :

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

Vielleicht haben Sie ein paar Funktionen , die einfach nicht , gehören in den fileutils, procutilsoder parseutilsModule. Angenommen, Sie möchten kein neues Modul namens erstellen miscutils. UND, Sie möchten die Funktion folgendermaßen aufrufen können:

erikutils.foo()
erikutils.bar()

anstatt zu tun

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

Da das erikutilsModul also ein Verzeichnis und keine Datei ist, müssen wir seine Funktionen innerhalb von definieren__init__.py Datei definieren.

In django, ist das beste Beispiel , das ich denken kann django.db.models.fields. ALLE django * -Feldklassen werden in der __init__.pyDatei im Verzeichnis django / db / models / fields definiert . Ich denke, sie haben dies getan, weil sie nicht alles in ein hypothetisches django / db / models / fields.py- Modell packen wollten , also haben sie es in einige Submodule aufgeteilt ( related.py , files.py zum Beispiel) und Sie haben die gemachten * Felddefinitionen in das Feldmodul selbst gesteckt (daher __init__.py).

dgrant
quelle
1
dgrant, was ich damit gemeint habe ist, dass es somethingsich um ein externes Modul handeln kann, dir.something wird noch funktionieren. Vielen Dank für den Kommentar, ich werde meinen Beitrag bearbeiten, um ihn klarer zu machen.
Alexander Kojevnikov
29

Durch die Verwendung der __init__.pyDatei können Sie die interne Paketstruktur von außen unsichtbar machen. Wenn sich die interne Struktur ändert (z. B. weil Sie ein Fettmodul in zwei geteilt haben), müssen Sie nur die anpassen__init__.py Datei , nicht jedoch den Code, der vom Paket abhängt. Sie können auch Teile Ihres Pakets unsichtbar machen, z. B. wenn sie nicht für den allgemeinen Gebrauch bereit sind.

Beachten Sie, dass Sie den delBefehl verwenden können, sodass ein typischer Befehl folgendermaßen __init__.pyaussehen kann:

from somemodule import some_function1, some_function2, SomeObject

del somemodule

Wenn Sie sich jetzt entscheiden, somemoduledas Neue zu teilen, __init__.pykönnte dies sein:

from somemodule1 import some_function1, some_function2
from somemodule2 import SomeObject

del somemodule1
del somemodule2

Von außen sieht das Paket immer noch genauso aus wie zuvor.

Nikow
quelle
1
@Arlen: Der Punkt ist, dass es nicht Teil der öffentlichen API ist. Wenn Sie ein Modul umbenennen, können Sie sicher sein, dass kein abhängiger Code unterbrochen wird. Darüber hinaus wird sichergestellt, dass die API-Elemente nur einmal angezeigt werden, z. B. wenn Introspection zum automatischen Erstellen der API-Dokumentation verwendet wird.
Nikow
5
@Arlen: Das Löschen eines Moduls verhindert, dass eines import <pack>.somemodule1direkt ausgeführt wird. Sie können nur aus <pack>Objekten importieren, die in ihren __init__.pySubmodulen definiert oder importiert wurden , sowie aus nicht gelöschten Submodulen.
MestreLion