Ist __init__.py für Pakete in Python 3.3+ nicht erforderlich?

193

Ich benutze Python 3.5.1. Ich habe das Dokument und den Paketabschnitt hier gelesen: https://docs.python.org/3/tutorial/modules.html#packages

Jetzt habe ich folgende Struktur:

/home/wujek/Playground/a/b/module.py

module.py::

class Foo:
    def __init__(self):
        print('initializing Foo')

Jetzt, während in /home/wujek/Playground:

~/Playground $ python3
>>> import a.b.module
>>> a.b.module.Foo()
initializing Foo
<a.b.module.Foo object at 0x100a8f0b8>

In ähnlicher Weise, jetzt zu Hause, Superordner von Playground:

~ $ PYTHONPATH=Playground python3
>>> import a.b.module
>>> a.b.module.Foo()
initializing Foo
<a.b.module.Foo object at 0x10a5fee10>

Eigentlich kann ich alles Mögliche machen:

~ $ PYTHONPATH=Playground python3
>>> import a
>>> import a.b
>>> import Playground.a.b

Warum funktioniert das? Ich obwohl es erforderlich sein , __init__.pyDateien in beide (leeren funktionieren würde) aund bfür module.pyimportierbar zu sein , wenn die Python - Pfad verweist auf den PlaygroundOrdner?

Dies scheint sich gegenüber Python 2.7 geändert zu haben:

~ $ PYTHONPATH=Playground python
>>> import a
ImportError: No module named a
>>> import a.b
ImportError: No module named a.b
>>> import a.b.module
ImportError: No module named a.b.module

Mit __init__.pyin beiden ~/Playground/aund ~/Playground/a/bes funktioniert gut.

Wujek
quelle

Antworten:

190

Python 3.3+ verfügt über implizite Namespace-Pakete , mit denen Pakete ohne __init__.pyDatei erstellt werden können.

Das Zulassen impliziter Namespace-Pakete bedeutet, dass die Anforderung zur Bereitstellung einer __init__.pyDatei vollständig gelöscht und beeinflusst werden kann.

Der alte Weg mit __init__.pyDateien funktioniert immer noch wie in Python 2.

Mike Müller
quelle
10
Ich werde das Dokument lesen, aber es ist ein bisschen lang. Ist es möglich, schnell zusammenzufassen? Könnten Sie mir einfach sagen: Unterstützt es noch init .py oder ignoriert es sie vollständig? Wenn es sie unterstützt, was ist der Unterschied in der Funktionalität und warum diese Dualität?
Wujek
3
Das Tutorial sollte also wahrscheinlich aktualisiert werden. Ist ein Dokumentationsfehler dafür geöffnet?
Michel Samia
4
Ich bin immer noch verärgert, dass dies der Zen Of Python Zeile 2 trotzt : Explicit is better than implicit.....
JayRizzo
4
@ JayRizzo Aber: "Obwohl Praktikabilität Reinheit schlägt."
Mike Müller
18
@ JayRizzo IMO ist es noch expliziter. Manchmal macht es Init-Sachen __init__.py, manchmal nicht. Wenn ich in Python 3 diese Dinge brauche, erstelle ich einen neuen __init__.pymit spezifischem Code, sonst nicht. Dies ist praktisch, um visuell zu wissen, welche Pakete eine benutzerdefinierte Init haben. Stattdessen muss ich in Python 2 immer ein __init__.py(oft leeres) platzieren, wodurch eine große Anzahl von ihnen erstellt wird und es schließlich schwieriger wird, sich daran zu erinnern, wo Sie Ihren Init-Code platziert haben. Dies sollte auch passen "Es sollte einen - und vorzugsweise nur einen - offensichtlichen Weg geben, dies zu tun."
Paolo
146

WICHTIG

@ Mikes Antwort ist richtig, aber zu ungenau. Es stimmt, dass Python 3.3+ implizite Namespace-Pakete unterstützt , mit denen ein Paket ohne __init__.pyDatei erstellt werden kann.

Dies gilt jedoch NUR für leere__init__.py Dateien. So LEER__init__.py - Dateien sind nicht mehr notwendig und kann weggelassen werden. Wenn Sie beim Importieren des Pakets oder eines seiner Module oder Unterpakete ein bestimmtes Initialisierungsskript ausführen möchten, benötigen Sie weiterhin ein__init__.py Datei. Dies ist eine großartige Antwort auf den Stapelüberlauf, warum Sie eine __init__.pyDatei verwenden möchten, um eine weitere Initialisierung durchzuführen, falls Sie sich fragen, warum dies in irgendeiner Weise nützlich ist.

Beispiel für die Verzeichnisstruktur:

  parent_package/
     __init__.py            <- EMPTY, NOT NECESSARY in Python 3.3+
     child_package/
          __init__.py       <- STILL REQUIRED if you want to run an initialization script
          child1.py
          child2.py
          child3.py

parent_package/child_package/__init__.py::

print("from parent")

BEISPIELE

Die folgenden Beispiele zeigen, wie das Initialisierungsskript ausgeführt wird, wenn das child_package oder eines seiner Module importiert wird.

Beispiel 1 :

from parent_package import child_package  # prints "from parent"

Beispiel 2 :

from parent_package.child_package import child1  # prints "from parent"
arauter
quelle
2
Angenommen, ich habe das run_script.pygleiche Verzeichnis wie parent_packagekann ich einfach wie from parent_package.child_package import child1ohne importieren __init__.py?
Mrgloom
Ist dies der Zweck, damit Sie child_package.some_function schreiben können, auch wenn some_function in childX.py definiert ist? Mit anderen Worten, es wird vermieden, dass der Benutzer die verschiedenen Dateien in child_package kennen muss. ?
Johnbakers
Ja, ich verstehe nicht, warum Sie machen würden child1.py, child2.pyanstatt nur ihren Code __init__direkt in .py zusammenzustellen.
Binki
Sollten die Importanweisungen __init__nicht relative Importe sein, dh from . import child1? Der absolute Import gibt mir ModuleNotFoundError(in Python 3.6)
Halbeard
5
Nach meiner Erfahrung wird selbst mit Python 3.3+ __init__.pymanchmal noch ein Leerzeichen benötigt, beispielsweise wenn Sie einen Unterordner als Paket bezeichnen möchten. Wenn ich zum Beispiel ausgeführt habe, hat python -m test.fooes nicht funktioniert, bis ich ein leeres __init__.pyunter dem Testordner erstellt habe. Und ich spreche hier von der Version 3.6.6!
Prahlad Yeri
6

Wenn Sie setup.pyin Ihrem Projekt haben und es darin verwenden find_packages(), ist es erforderlich, __init__.pyin jedem Verzeichnis eine Datei zu haben, damit Pakete automatisch gefunden werden.

Pakete werden nur erkannt, wenn sie eine __init__.pyDatei enthalten

UPD : Wenn Sie implizite Namespace-Pakete verwenden möchten, ohne diese __init__.pynur verwenden zu müssenfind_namespace_packages() stattdessen verwenden zu müssen

Docs

techkuz
quelle
1

Ich würde sagen, dass man das __init__.pynur weglassen sollte, wenn man das implizite Namespace-Paket haben möchte . Wenn Sie nicht wissen, was es bedeutet, möchten Sie es wahrscheinlich nicht und sollten daher das __init__.pyEven in Python 3 weiterhin verwenden.

Mi-La
quelle