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?

War es hilfreich?

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 Aussage from sound.effects import * tut nicht Importieren Sie alle Submodule aus dem Paket sound.effects in den aktuellen Namensraum;Es stellt nur sicher, dass das Paket sound.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__, Dann from foo import * importiert nur Namen, die in definiert sind foo/__init__.py.

  • (Explizit) Wenn wir definieren __all__ = [], Dann from foo import * wird nichts importieren.

  • (Explizit) Wenn wir definieren __all__ = [ <name1>, ... ], Dann from 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 sollen from 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',)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top