Kann jemand __all__ in Python erklären?
-
09-06-2019 - |
Frage
Ich verwende Python immer häufiger und sehe die Variable immer wieder __all__
unterschiedlich eingestellt __init__.py
Dateien.Kann jemand erklären, was das bewirkt?
Lösung
Es handelt sich um eine Liste öffentlicher Objekte dieses Moduls, wie sie von interpretiert werden import *
.Es überschreibt die Standardeinstellung, alles auszublenden, was mit einem Unterstrich beginnt.
Andere Tipps
Der genaue Zeitpunkt ist verlinkt, wird hier aber nicht explizit erwähnt __all__
wird eingesetzt.Es handelt sich um eine Liste von Zeichenfolgen, die definieren, welche Symbole in einem Modul wann exportiert werden from <module> import *
wird auf dem Modul verwendet.
Zum Beispiel der folgende Code in a foo.py
exportiert die Symbole explizit bar
Und baz
:
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): return 'baz'
Diese Symbole können dann wie folgt importiert werden:
from foo import *
print(bar)
print(baz)
# The following will trigger an exception, as "waz" is not exported by the module
print(waz)
Wenn die __all__
Wenn das Obige auskommentiert ist, wird dieser Code dann vollständig ausgeführt, wie es das Standardverhalten von ist import *
besteht darin, alle Symbole, die nicht mit einem Unterstrich beginnen, aus dem angegebenen Namensraum zu importieren.
Referenz: https://docs.python.org/tutorial/modules.html#importing-from-a-package
NOTIZ: __all__
beeinflusst die from <module> import *
nur Verhalten.Mitglieder, die in nicht erwähnt werden __all__
sind weiterhin von außerhalb des Moduls zugänglich und können mit importiert werden from <module> import <member>
.
Ich füge dies nur hinzu, um genau zu sein:
Alle anderen Antworten beziehen sich auf Module.Die ursprüngliche Frage wurde ausdrücklich erwähnt __all__
In __init__.py
Dateien, hier geht es also um Python Pakete.
Allgemein, __all__
kommt erst ins Spiel, wenn die from xxx import *
Variante des import
Anweisung verwendet wird.Dies gilt sowohl für Pakete als auch für Module.
Das Verhalten für Module wird in den anderen Antworten erläutert.Das genaue Verhalten für Pakete wird beschrieben Hier im Detail.
Zusamenfassend, __all__
auf Paketebene macht ungefähr das Gleiche wie für Module, außer dass es sich um Folgendes handelt Module innerhalb des Pakets (im Gegensatz zur Angabe Namen innerhalb des Moduls).Also __all__
Gibt alle Module an, die bei unserer Verwendung geladen und in den aktuellen Namespace importiert werden sollen from package import *
.
Der große Unterschied besteht darin, dass, wenn Sie auslassen die Erklärung von __all__
im Paket __init__.py
, die Aussage from package import *
importiert überhaupt nichts (mit Ausnahmen, die in der Dokumentation erläutert werden, siehe Link oben).
Auf der anderen Seite, wenn Sie es weglassen __all__
In einem Modul importiert der „Sternimport“ alle im Modul definierten Namen (die nicht mit einem Unterstrich beginnen).
__all__ in Python erklären?
Ich sehe die Variable immer wieder
__all__
unterschiedlich eingestellt__init__.py
Dateien.Was bewirkt das?
Was macht __all__
Tun?
Es deklariert die semantisch „öffentlichen“ Namen eines Moduls.Wenn ein Name drin ist __all__
, Von den Benutzern wird erwartet, dass sie es verwenden, und sie können erwarten, dass es sich nicht ändert.
Es wird auch programmatische Auswirkungen haben:
import *
__all__
in einem Modul, z.B. module.py
:
__all__ = ['foo', 'Bar']
bedeutet das, wenn du import *
Aus dem Modul werden nur die Namen in der __all__
werden importiert:
from module import * # imports foo and Bar
Dokumentationstools
Dokumentations- und Code-Autovervollständigungstools können (sollten) auch die überprüfen __all__
um zu bestimmen, welche Namen in einem Modul als verfügbar angezeigt werden sollen.
__init__.py
macht ein Verzeichnis zu einem Python-Paket
Von dem Dokumente:
Der
__init__.py
Dateien sind erforderlich, damit Python die Verzeichnisse so behandelt, als ob sie Pakete enthalten.Dies geschieht, um zu verhindern, dass Verzeichnisse mit einem gemeinsamen Namen, z. B. einer Zeichenfolge, unbeabsichtigt gültige Module verbergen, die später im Modulsuchpfad auftreten.Im einfachsten Fall
__init__.py
kann nur eine leere Datei sein, aber es kann auch Initialisierungscode für das Paket ausführen oder festlegen__all__
Variable.
Also die __init__.py
kann das erklären __all__
Für ein Paket.
Verwalten einer API:
Ein Paket besteht typischerweise aus Modulen, die sich gegenseitig importieren können, aber notwendigerweise durch ein Modul miteinander verbunden sind __init__.py
Datei.Diese Datei macht das Verzeichnis zu einem tatsächlichen Python-Paket.Angenommen, Sie haben Folgendes:
package/
|-__init__.py # makes directory a Python package
|-module_1.py
|-module_2.py
im __init__.py
du schreibst:
from module_1 import *
from module_2 import *
und in module_1
du hast:
__all__ = ['foo',]
und in module_2
du hast:
__all__ = ['Bar',]
Und jetzt haben Sie eine vollständige API präsentiert, die jemand anderes verwenden kann, wenn er Ihr Paket importiert, etwa so:
import package
package.foo()
package.Bar()
Und sie werden nicht alle anderen Namen haben, die Sie beim Erstellen Ihrer Module verwendet haben package
Namensraum.
__all__
In __init__.py
Nach mehr Arbeit haben Sie vielleicht entschieden, dass die Module zu groß sind und aufgeteilt werden müssen.Sie gehen also wie folgt vor:
package/
|-__init__.py
|-module_1/
| |-__init__.py
| |-foo_implementation.py
|-module_2/
|-__init__.py
|-Bar_implementation.py
Und in jedem __init__.py
Sie erklären eine __all__
, z.B.im Modul_1:
from foo_implementation import *
__all__ = ['foo']
Und Modul_2 __init__.py
:
from Bar_implementation import *
__all__ = ['Bar']
Und Sie können Ihrer API ganz einfach Dinge hinzufügen, die Sie auf Unterpaketebene statt auf Modulebene des Unterpakets verwalten können.Wenn Sie der API einen neuen Namen hinzufügen möchten, aktualisieren Sie einfach den __init__.py
, z.B.im Modul_2:
from Bar_implementation import *
from Baz_implementation import *
__all__ = ['Bar', 'Baz']
Und wenn Sie nicht zur Veröffentlichung bereit sind Baz
in der Top-Level-API, in Ihrer Top-Level __init__.py
du könntest haben:
from module_1 import * # also constrained by __all__'s
from module_2 import * # in the __init__.py's
__all__ = ['foo', 'Bar'] # further constraining the names advertised
und ob Ihre Benutzer sich der Verfügbarkeit von bewusst sind Baz
, sie können es verwenden:
import package
package.Baz()
aber wenn sie nichts davon wissen, können andere Tools (wie pydoc) wird sie nicht informieren.
Sie können das später jederzeit ändern Baz
ist bereit für die Hauptsendezeit:
from module_1 import *
from module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
Präfix _
gegen __all__
:
Standardmäßig exportiert Python alle Namen, die nicht mit einem beginnen _
.Du sicherlich könnte Verlassen Sie sich auf diesen Mechanismus.Einige Pakete in der Python-Standardbibliothek sind tatsächlich Tun verlassen sich darauf, aber um dies zu tun, geben sie ihren Importen beispielsweise einen Alias in ctypes/__init__.py
:
import os as _os, sys as _sys
Verwendung der _
Die Konvention kann eleganter sein, da sie die Redundanz einer erneuten Benennung der Namen beseitigt.Aber es fügt die Redundanz für Importe hinzu (wenn Sie viele davon haben) und das ist auch so einfach zu vergessen, dies konsequent zu tun – und das Letzte, was Sie wollen, ist, auf unbestimmte Zeit etwas unterstützen zu müssen, von dem Sie gedacht haben, dass es sich nur um ein Implementierungsdetail handelt, nur weil Sie vergessen haben, ein voranzustellen _
beim Benennen einer Funktion.
Ich persönlich schreibe eine __all__
Früh in meinem Entwicklungslebenszyklus für Module, damit andere, die meinen Code verwenden könnten, wissen, was sie verwenden und was nicht.
Die meisten Pakete in der Standardbibliothek verwenden ebenfalls __all__
.
Beim Vermeiden __all__
macht Sinn
Es ist sinnvoll, dabei zu bleiben _
Präfixkonvention anstelle von __all__
Wann:
- Sie befinden sich noch im frühen Entwicklungsmodus, haben keine Benutzer und optimieren ständig Ihre API.
- Vielleicht haben Sie zwar Benutzer, aber Sie haben Unittests, die die API abdecken, und Sie erweitern die API immer noch aktiv und optimieren die Entwicklung.
Ein export
Dekorateur
Der Nachteil der Verwendung __all__
besteht darin, dass Sie die Namen der zu exportierenden Funktionen und Klassen zweimal schreiben müssen – und die Informationen von den Definitionen getrennt bleiben.Wir könnte Verwenden Sie einen Dekorateur, um dieses Problem zu lösen.
Die Idee für einen solchen Exportdekorateur kam mir durch David Beazleys Vortrag über Verpackungen.Diese Implementierung scheint im traditionellen Importer von CPython gut zu funktionieren.Wenn Sie über einen speziellen Import-Hook oder ein spezielles Importsystem verfügen, kann ich dies nicht garantieren, aber wenn Sie es übernehmen, ist das Zurücksetzen ziemlich einfach – Sie müssen die Namen einfach manuell wieder hinzufügen __all__
So würden Sie beispielsweise in einer Dienstprogrammbibliothek den Dekorator wie folgt definieren:
import sys
def export(fn):
mod = sys.modules[fn.__module__]
if hasattr(mod, '__all__'):
mod.__all__.append(fn.__name__)
else:
mod.__all__ = [fn.__name__]
return fn
und dann, wo Sie ein definieren würden __all__
, Du machst das:
$ cat > main.py
from lib import export
__all__ = [] # optional - we create a list if __all__ is not there.
@export
def foo(): pass
@export
def bar():
'bar'
def main():
print('main')
if __name__ == '__main__':
main()
Und das funktioniert gut, egal ob es als Hauptfunktion ausgeführt oder von einer anderen Funktion importiert wird.
$ cat > run.py
import main
main.main()
$ python run.py
main
Und API-Bereitstellung mit import *
wird auch funktionieren:
$ cat > run.py
from main import *
foo()
bar()
main() # expected to error here, not exported
$ python run.py
Traceback (most recent call last):
File "run.py", line 4, in <module>
main() # expected to error here, not exported
NameError: name 'main' is not defined
Es ändert auch, was pydoc anzeigt:
module1.py
a = "A"
b = "B"
c = "C"
module2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$ pydoc module1
Help on module module1: NAME module1 DATEI module1.py DATEN A = 'A' B = 'B' C = 'C'
$ pydoc module2
Help on module module2: NAME module2 DATEI module2.py DATEN __alle__ = ['a', 'b'] A = 'A' B = 'B'
Ich erkläre __all__
In allen meinen Modulen sind diese Elemente nicht nur unterstrichen, sondern auch interne Details. Sie helfen wirklich dabei, Dinge zu verwenden, die Sie noch nie zuvor in Live-Dolmetschersitzungen verwendet haben.
Aus (Ein inoffizielles) Python-Referenz-Wiki:
Die von einem Modul definierten öffentlichen Namen werden ermittelt, indem der Namespace des Moduls auf eine Variable mit dem Namen überprüft wird
__all__
;Falls definiert, muss es sich um eine Folge von Zeichenfolgen handeln, bei denen es sich um Namen handelt, die von diesem Modul definiert oder importiert wurden.Die angegebenen Namen__all__
gelten alle als öffentlich und müssen existieren.Wenn__all__
nicht definiert ist, umfasst der Satz öffentlicher Namen alle im Namensraum des Moduls gefundenen Namen, die nicht mit einem Unterstrich („_“) beginnen.__all__
sollte die gesamte öffentliche API enthalten.Dadurch soll verhindert werden, dass versehentlich Elemente exportiert werden, die nicht Teil der API sind (z. B. Bibliotheksmodule, die importiert und innerhalb des Moduls verwendet wurden).
__all__
passt sich an das Sternchen In from <module> import *
__all__
passt sich an das Sternchen In from <package> import *
A Modul ist ein .py
Datei, die importiert werden soll.
A Paket ist ein Verzeichnis mit a __init__.py
Datei.Ein Paket enthält normalerweise Module.
MODULE
""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99
cheddar = 3.99
gouda = 10.99
__all__
informiert Menschen über die „öffentlichen“ Merkmale eines Modul.[@AaronHall] Außerdem erkennt pydoc sie.[@Longpoke]
aus Modul importieren *
Siehe wie swiss
Und cheddar
werden in den lokalen Namensraum gebracht, aber nicht gouda
:
>>> from cheese import *
>>> swiss, cheddar
(4.99, 3.99)
>>> gouda
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'gouda' is not defined
Ohne __all__
, wäre jedes Symbol (das nicht mit einem Unterstrich beginnt) verfügbar gewesen.
Importe ohne *
sind davon nicht betroffen __all__
importieren Modul
>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)
aus Modul importieren Namen
>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)
importieren Modul als Lokaler Name
>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)
PAKETE
Im __init__.py
Akte eines Paket __all__
ist eine Liste von Zeichenfolgen mit den Namen öffentlicher Module oder anderer Objekte.Diese Funktionen stehen für Wildcard-Importe zur Verfügung.Wie bei Modulen, __all__
Passt die an *
beim Wildcard-Import aus dem Paket.[@MartinStettner]
Hier ist ein Auszug aus dem Python-MySQL-Connector __init__.py
:
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
Der Standardfall, Sternchen mit Nr __all__
für ein Paket, ist kompliziert, weil das offensichtliche Verhalten teuer wäre:um das Dateisystem zu verwenden, um nach allen Modulen im Paket zu suchen.Stattdessen wurden in meiner Lektüre der Dokumente nur die in definierten Objekte aufgeführt __init__.py
werden importiert:
Wenn
__all__
ist nicht definiert, die Aussagefrom sound.effects import *
tut nicht Importieren Sie alle Submodule aus dem Paketsound.effects
in den aktuellen Namensraum;Es stellt nur sicher, dass das Paketsound.effects
wurde importiert (möglicherweise wird ein Initialisierungscode ausgeführt).__init__.py
) und importiert dann alle im Paket definierten Namen.Dazu gehören alle von definierten Namen (und explizit geladenen Submodule).__init__.py
.Es umfasst auch alle Submodule des Pakets, die explizit durch vorherige Importanweisungen geladen wurden.
Wildcard-Importe ...sollten vermieden werden, da sie Leser und viele automatisierte Tools [verwirren].
[PEP 8, @ToolmakerSteve]
Kurze Antwort
__all__
wirkt from <module> import *
Aussagen.
Lange Antwort
Betrachten Sie dieses Beispiel:
foo
├── bar.py
└── __init__.py
In foo/__init__.py
:
(Implizit) Wenn wir nicht definieren
__all__
, Dannfrom foo import *
importiert nur Namen, die in definiert sindfoo/__init__.py
.(Explizit) Wenn wir definieren
__all__ = []
, Dannfrom foo import *
wird nichts importieren.(Explizit) Wenn wir definieren
__all__ = [ <name1>, ... ]
, Dannfrom foo import *
importiert nur diese Namen.
Beachten Sie, dass Python im impliziten Fall keine Namen importiert, die mit beginnen _
.Sie können den Import solcher Namen jedoch mit erzwingen __all__
.
Sie können das Python-Dokument anzeigen Hier.
__all__
wird verwendet, um die öffentliche API eines Python-Moduls zu dokumentieren.Obwohl es optional ist, __all__
sollte benutzt werden.
Hier ist der entsprechende Auszug aus die Python-Sprachreferenz:
Die von einem Modul definierten öffentlichen Namen werden ermittelt, indem der Namespace des Moduls auf eine Variable mit dem Namen überprüft wird
__all__
;Falls definiert, muss es sich um eine Folge von Zeichenfolgen handeln, bei denen es sich um Namen handelt, die von diesem Modul definiert oder importiert wurden.Die angegebenen Namen__all__
gelten alle als öffentlich und müssen existieren.Wenn__all__
nicht definiert ist, umfasst der Satz öffentlicher Namen alle im Namensraum des Moduls gefundenen Namen, die nicht mit einem Unterstrich ('_') beginnen.__all__
sollte die gesamte öffentliche API enthalten.Dadurch soll verhindert werden, dass versehentlich Elemente exportiert werden, die nicht Teil der API sind (z. B. Bibliotheksmodule, die importiert und innerhalb des Moduls verwendet wurden).
PEP 8 verwendet einen ähnlichen Wortlaut, stellt jedoch auch klar, dass importierte Namen nicht Teil der öffentlichen API sind, wenn __all__
ist abwesend:
Um die Introspektion besser zu unterstützen, sollten Module die Namen in ihrer öffentlichen API explizit mit deklarieren
__all__
Attribut.Einstellung__all__
zu einer leeren Liste zeigt an, dass das Modul keine öffentliche API hat.[...]
Importierte Namen sollten immer als Implementierungsdetail betrachtet werden.Andere Module dürfen nicht auf indirekten Zugriff auf solche importierten Namen angewiesen sein, es sei denn, sie sind ein explizit dokumentierter Teil der API des enthaltenden Moduls, wie z
os.path
oder ein Paket__init__
Modul, das die Funktionalität von Submodulen verfügbar macht.
Darüber hinaus wurde, wie bereits in anderen Antworten erwähnt, __all__
dient zur Aktivierung Wildcard-Import für Pakete:
Die Importanweisung verwendet die folgende Konvention:wenn ein Paket
__init__.py
Code definiert eine Liste mit dem Namen__all__
, wird davon ausgegangen, dass es sich um die Liste der Modulnamen handelt, die wann importiert werden sollenfrom package import *
angetroffen wird.
Zusätzlich zu den vorhandenen Antworten, __all__
muss keine Liste sein.Gemäß der Dokumentation zum import
Stellungnahme, falls definiert, __all__
muss ein sein Folge von Zeichenfolgen Dabei handelt es sich um vom Modul definierte oder importierte Namen.Sie können also genauso gut ein Tupel verwenden speichern einige Speicher- und CPU-Zyklen.Vergessen Sie nur nicht ein Komma, falls das Modul einen einzelnen öffentlichen Namen definiert:
__all__ = ('some_name',)