Wie schreibe ich gute / korrekte Paketdateien __init__.py?


187

Mein Paket hat folgende Struktur:

mobilescouter/
    __init__.py #1
    mapper/
        __init__.py  #2
        lxml/
            __init__.py #3
            vehiclemapper.py
            vehiclefeaturemapper.py
            vehiclefeaturesetmapper.py
        ...
        basemapper.py
   vehicle/
        __init__.py #4
        vehicle.py
        vehiclefeature.py
        vehiclefeaturemapper.py
   ...

Ich bin nicht sicher, wie die __init__.pyDateien richtig geschrieben werden sollen.
Das __init__.py #1sieht aus wie:

__all__ = ['mapper', 'vehicle']
import mapper
import vehicle

Aber wie soll das zum Beispiel __init__.py #2aussehen? Meins ist:

__all__ = ['basemapper', 'lxml']
from basemaper import *
import lxml

Wann sollte __all__verwendet werden?


2
Beachten Sie jedoch, dass die Verwendung von Import * in Code im Allgemeinen eine sehr schlechte Praxis ist und nach Möglichkeit vermieden werden sollte. Es gibt nur sehr wenige gute Anwendungsfälle dafür, aber sie sind wirklich selten.
Mayou36

PSA: Wenn Sie lernen möchten, wie man gute Namespace-Pakete (die neue Art von Paket) schreibt, lesen Sie dieses Beispielpaket: github.com/pypa/sample-namespace-packages
Kyle

Antworten:


145

__all__ ist sehr gut - es hilft beim Importieren von Anweisungen, ohne Module automatisch zu importieren http://docs.python.org/tutorial/modules.html#importing-from-a-package

Verwenden __all__und import *ist nur redundant__all__ benötigt

Ich glaube , einer der stärksten Gründe für den Einsatz import *in einem __init__.pyzu Importpaket in der Lage sein , einen Skript Refactoring , die in mehrere Skripte , ohne zu brechen eine bestehende Anwendung gewachsen ist. Aber wenn Sie von Anfang an ein Paket entwerfen. Ich denke, es ist am besten, __init__.pyDateien leer zu lassen.

beispielsweise:

foo.py - contains classes related to foo such as fooFactory, tallFoo, shortFoo

dann wächst die App und jetzt ist es ein ganzer Ordner

foo/
    __init__.py
    foofactories.py
    tallFoos.py
    shortfoos.py
    mediumfoos.py
    santaslittlehelperfoo.py
    superawsomefoo.py
    anotherfoo.py

dann kann das init-Skript sagen

__all__ = ['foofactories', 'tallFoos', 'shortfoos', 'medumfoos',
           'santaslittlehelperfoo', 'superawsomefoo', 'anotherfoo']
# deprecated to keep older scripts who import this from breaking
from foo.foofactories import fooFactory
from foo.tallfoos import tallFoo
from foo.shortfoos import shortFoo

Damit ein Skript, das für Folgendes geschrieben wurde, während der Änderung nicht unterbrochen wird:

from foo import fooFactory, tallFoo, shortFoo

3
Ich war sehr verwirrt über " alle " und den zeilenweisen Import. Ihr Beispiel ist sehr aufschlussreich.
Junchen

2
Ich bin verwirrt von " __all__und import *ist redundant", __all__wird vom Verbraucher des Moduls verwendet und from foo import *wird vom Modul selbst verwendet, um andere zu verwenden ....
Nick T

using __all__ and import * is redundant, only __all__ is needed Wie sind diese überflüssig? Sie machen verschiedene Dinge.
Endolith

112

Meine eigenen __init__.pyDateien sind meistens leer. Insbesondere habe ich nie einen from blah import *Teil von __init__.py- wenn "Importieren des Pakets" bedeutet, dass alle Arten von Klassen, Funktionen usw. direkt als Teil des Pakets definiert werden, würde ich stattdessen den Inhalt von lexikalisch blah.pyin das Paket kopieren __init__.pyund entfernen blah.py( Die Multiplikation von Quelldateien nützt hier nichts.

Wenn Sie darauf bestehen, die import *Redewendungen (eek) zu unterstützen, kann die Verwendung __all__(mit einer möglichst winzigen Liste von Namen, die Sie selbst verwenden können) zur Schadensbegrenzung hilfreich sein. Im Allgemeinen sind Namespaces und explizite Importe gute Dinge, und ich empfehle dringend, jeden Ansatz zu überdenken, der auf der systematischen Umgehung eines oder beider Konzepte basiert! -)


9
Persönlich ziehe ich es vor, die Dinge getrennt zu halten und dann zu importieren *. Der Grund ist, dass ich es trotz Falten und so immer noch hasse, Dateien zu durchsuchen, die zu viele Klassen enthalten, auch wenn sie verwandt sind.
Stefano Borini

5
@stefano denke über ein großes Framework nach. Wenn es verwendet wird import *, müssen Sie bedingungslos das gesamte Framework akzeptieren, auch Funktionen, die Sie niemals verwenden werden. Wenn Sie __init__.pyleer bleiben, haben Sie mehr Chancen als nur Alles-oder-Nichts-Semantik. Denken Sie an verdreht.
mg.

Wenn Sie es auch nach dem Import von mobilescouter leer lassen, können Sie mobilescouter.mapper oder mobilescouter.vehicle oder mobilescouter.was auch immer nicht verwenden. ist nicht import mobilescouter.A, mobilescouter.B ..... zu ausführlich?
Sunqiang

6
@sunqiang das ist persönlich, aber ich denke nicht. from mobilescouter import A, Bist nur eine Codezeile und Sie haben kein Projekt mit 666 Klassen und jeder mit seiner eigenen Datei, oder? Wenn Sie zwei oder mehr import *in Ihrem Code haben, füllen Sie den Namespace mit potenziellem Müll und Sie werden schnell vergessen, woher Sie Akommen. Und wenn ein oberes Paket dasselbe tut? Sie greifen nach allen Unterpaketen und Unterunterpaketen. Wie der Zen von Python sagt, ist explizit besser als implizit.
mg.

1
@mg, wenn die init .py-Datei eine Zeile "A, B importieren" enthält, kann ich das A (oder B) mit der folgenden Syntax aufrufen: mobilescouter.A; Wenn wir "von mobilescouter importieren A, B" verwenden, dann ist es nur A. etwas. Manchmal nur diese Zeile, ich erinnere mich nicht, dass A ein Teilpaket von mobilescouter ist, und ich denke, dass dies zur Verschmutzung des Namespace beiträgt (obwohl es viel besser ist als "" von mobilescouter import * ". Ich bevorzuge immer noch" import pkgname ", um Benutzer zu geben die einheitliche öffentliche Schnittstelle. also init .py die import sub_pkgname Dinge tun.
sunqiang

1

Sie __init__.pysollten einen Docstring haben .

Obwohl alle Funktionen in Modulen und Unterpaketen implementiert sind, können Sie in Ihrem Paket docstring dokumentieren, wo Sie beginnen sollen. Betrachten Sie beispielsweise das Python- emailPaket . Die Paketdokumentation ist eine Einführung, die den Zweck, den Hintergrund und die Zusammenarbeit der verschiedenen Komponenten im Paket beschreibt. Wenn Sie automatisch Dokumentation aus Docstrings mit Sphinx oder einem anderen Paket generieren, ist das Paket Docstring genau der richtige Ort, um eine solche Einführung zu beschreiben.

Weitere Informationen finden Sie in den hervorragenden Antworten von Firecrow und Alex Martelli .


Entspricht der tatsächliche Wert __init__.pyfür das emailPaket dieser Richtlinie? Ich sehe eine einzeilige Dokumentzeichenfolge, die nicht viel dazu beiträgt, zu erklären, "wie die verschiedenen Komponenten im Paket zusammenarbeiten".
Gertlex

@Gertlex Vielleicht nur in der Webdokumentation.
Gerrit
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.