Qualcuno può spiegare __all__ in Python?


983

Ho sempre usato Python e continuo a vedere la variabile __all__impostata in diversi __init__.pyfile. Qualcuno può spiegare cosa fa questo?

Risposte:


566

È un elenco di oggetti pubblici di quel modulo, come interpretato da import *. Sostituisce l'impostazione predefinita di nascondere tutto ciò che inizia con un carattere di sottolineatura.


146
Gli oggetti che iniziano con un carattere di sottolineatura, o che non sono menzionati in __all__se __all__presente, non sono esattamente nascosti; possono essere visti e consultati perfettamente normalmente se conosci i loro nomi. È solo nel caso di un "import *", che comunque non è raccomandato, che la distinzione abbia un peso.
Brandon Rhodes,

28
@BrandonRhodes: neanche questo è esattamente vero: si consiglia di importare solo i moduli per cui si sa che sono stati progettati import *(come ad es tk.). Un buon suggerimento in questo caso è la presenza __all__o nomi che iniziano con il trattino basso nel codice del modulo.
pecora volante

12
Interfacce pubbliche e interne - python.org/dev/peps/pep-0008/#id50 , Per supportare meglio l'introspezione, i moduli dovrebbero dichiarare esplicitamente i nomi nella loro API pubblica usando l'attributo __all__. L'impostazione di __all__ su un elenco vuoto indica che il modulo non ha API pubbliche.
debug

Non sono sicuro che se tkfossero stati rilasciati oggi (o anche nel 2012), la pratica raccomandata sarebbe quella di utilizzare from tk import *. Penso che la pratica sia accettata per inerzia, non per progettazione intenzionale.
Chepner,

Come sottolinea BrandonRhodes, questo in realtà non è corretto
duhaime,

947

Collegato a, ma non esplicitamente menzionato qui, è esattamente quando __all__viene utilizzato. È un elenco di stringhe che definisce quali simboli verranno esportati in un modulo quando from <module> import *viene utilizzato sul modulo.

Ad esempio, il codice seguente foo.pyesporta esplicitamente i simboli bare baz:

__all__ = ['bar', 'baz']

waz = 5
bar = 10
def baz(): return 'baz'

Questi simboli possono quindi essere importati in questo modo:

from foo import *

print(bar)
print(baz)

# The following will trigger an exception, as "waz" is not exported by the module
print(waz)

Se quanto __all__sopra è commentato, questo codice verrà eseguito fino al completamento, poiché il comportamento predefinito di import *è quello di importare tutti i simboli che non iniziano con un carattere di sottolineatura, dallo spazio dei nomi indicato.

Riferimento: https://docs.python.org/tutorial/modules.html#importing-from-a-package

NOTA: __all__ influisce from <module> import *solo sul comportamento. I membri non menzionati in __all__sono ancora accessibili dall'esterno del modulo e possono essere importati con from <module> import <member>.


1
non dovremmo stampare baz come print(baz())?
John Cole,

@JohnCole baz è oggetto funzione e baz () eseguirà l'oggetto funzione
Bhanu Tez,

@BhanuTez esattamente. Quindi print(baz)stampa qualcosa di simile <function baz at 0x7f32bc363c10>mentre print(baz())stampabaz
John Cole

223

Spiega __all__ in Python?

Continuo a vedere la variabile __all__impostata in diversi __init__.pyfile.

Cosa fa questo?

Cosa fa __all__?

Dichiara i nomi semanticamente "pubblici" da un modulo. Se è presente un nome __all__, gli utenti devono usarlo e possono aspettarsi che non cambi.

Avrà anche effetti programmatici:

import *

__all__in un modulo, ad esempio module.py:

__all__ = ['foo', 'Bar']

significa che quando vieni import *dal modulo, __all__vengono importati solo quei nomi nel :

from module import *               # imports foo and Bar

Strumenti di documentazione

Gli strumenti di completamento automatico della documentazione e del codice possono (in effetti, dovrebbero) ispezionare anche __all__per determinare quali nomi mostrare come disponibili da un modulo.

__init__.py rende una directory un pacchetto Python

Dai documenti :

I __init__.pyfile sono necessari per fare in modo che Python tratti le directory come pacchetti contenenti; ciò viene fatto per impedire alle directory con un nome comune, come stringa, di nascondere involontariamente moduli validi che si verificano successivamente nel percorso di ricerca del modulo.

Nel caso più semplice, __init__.pypuò essere solo un file vuoto, ma può anche eseguire il codice di inizializzazione per il pacchetto o impostare la __all__variabile.

Quindi __init__.pypuò dichiarare il __all__per un pacchetto .

Gestire un'API:

Un pacchetto è in genere costituito da moduli che possono importarsi a vicenda, ma che sono necessariamente collegati insieme a un __init__.pyfile. Quel file è ciò che rende la directory un vero pacchetto Python. Ad esempio, supponiamo di avere i seguenti file in un pacchetto:

package
├── __init__.py
├── module_1.py
└── module_2.py

Creiamo questi file con Python in modo da poter seguire insieme: potresti incollare quanto segue in una shell Python 3:

from pathlib import Path

package = Path('package')
package.mkdir()

(package / '__init__.py').write_text("""
from .module_1 import *
from .module_2 import *
""")

package_module_1 = package / 'module_1.py'
package_module_1.write_text("""
__all__ = ['foo']
imp_detail1 = imp_detail2 = imp_detail3 = None
def foo(): pass
""")

package_module_2 = package / 'module_2.py'
package_module_2.write_text("""
__all__ = ['Bar']
imp_detail1 = imp_detail2 = imp_detail3 = None
class Bar: pass
""")

E ora hai presentato un'API completa che qualcun altro può usare quando importano il tuo pacchetto, in questo modo:

import package
package.foo()
package.Bar()

E il pacchetto non avrà tutti gli altri dettagli di implementazione utilizzati durante la creazione dei moduli che ingombrano lo packagespazio dei nomi.

__all__ in __init__.py

Dopo ulteriori lavori, forse hai deciso che i moduli sono troppo grandi (come molte migliaia di linee?) E devono essere suddivisi. Quindi fai quanto segue:

package
├── __init__.py
├── module_1
│   ├── foo_implementation.py
│   └── __init__.py
└── module_2
    ├── Bar_implementation.py
    └── __init__.py

Per prima cosa crea le directory dei pacchetti secondari con gli stessi nomi dei moduli:

subpackage_1 = package / 'module_1'
subpackage_1.mkdir()
subpackage_2 = package / 'module_2'
subpackage_2.mkdir()

Sposta le implementazioni:

package_module_1.rename(subpackage_1 / 'foo_implementation.py')
package_module_2.rename(subpackage_2 / 'Bar_implementation.py')

creare __init__.pys per i pacchetti secondari che dichiarano __all__per ciascuno:

(subpackage_1 / '__init__.py').write_text("""
from .foo_implementation import *
__all__ = ['foo']
""")
(subpackage_2 / '__init__.py').write_text("""
from .Bar_implementation import *
__all__ = ['Bar']
""")

E ora hai ancora l'API fornito a livello di pacchetto:

>>> import package
>>> package.foo()
>>> package.Bar()
<package.module_2.Bar_implementation.Bar object at 0x7f0c2349d210>

E puoi facilmente aggiungere elementi all'API che puoi gestire a livello di subpackage anziché a livello di modulo del subpackage. Se si desidera aggiungere un nuovo nome all'API, è sufficiente aggiornare __init__.py, ad esempio in module_2:

from .Bar_implementation import *
from .Baz_implementation import *
__all__ = ['Bar', 'Baz']

E se non sei pronto a pubblicare Baznell'API di livello superiore, nel tuo livello superiore __init__.pypotresti avere:

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

e se i tuoi utenti sono a conoscenza della disponibilità di Baz, possono usarlo:

import package
package.Baz()

ma se non lo conoscono, altri strumenti (come pydoc ) non li informeranno.

In seguito puoi cambiarlo quando Bazè pronto per la prima serata:

from .module_1 import *
from .module_2 import *
__all__ = ['foo', 'Bar', 'Baz']

Prefisso _contro __all__:

Per impostazione predefinita, Python esporterà tutti i nomi che non iniziano con un _. Certamente potresti fare affidamento su questo meccanismo. Alcuni pacchetti nella libreria standard di Python, infatti, si basano su questo, ma per fare ciò, alias le loro importazioni, ad esempio in ctypes/__init__.py:

import os as _os, sys as _sys

L'uso della _convenzione può essere più elegante perché rimuove la ridondanza di denominare nuovamente i nomi. Ma aggiunge la ridondanza per le importazioni (se ne hai molte) ed è facile dimenticare di farlo in modo coerente - e l'ultima cosa che vuoi è dover supportare indefinitamente qualcosa che intendi essere solo un dettaglio di implementazione, solo perché hai dimenticato di aggiungere un prefisso _quando hai assegnato un nome a una funzione.

Personalmente scrivo una __all__prima parte del mio ciclo di vita di sviluppo per i moduli in modo che altri che potrebbero usare il mio codice sappiano cosa dovrebbero usare e non usare.

La maggior parte dei pacchetti nella libreria standard usa anche __all__.

Quando evitare __all__ha senso

Ha senso attenersi alla _convenzione del prefisso al posto di __all__quando:

  • Sei ancora in modalità di sviluppo iniziale e non hai utenti e stai costantemente modificando la tua API.
  • Forse hai utenti, ma hai unittest che coprono l'API e stai ancora attivamente aggiungendo all'API e ottimizzando lo sviluppo.

Un exportdecoratore

Il rovescio della medaglia dell'uso __all__è che devi scrivere i nomi delle funzioni e delle classi esportate due volte - e le informazioni sono mantenute separate dalle definizioni. Abbiamo potuto usare un decoratore per risolvere questo problema.

Ho avuto l'idea di un tale decoratore di esportazione dal discorso di David Beazley sul packaging. Questa implementazione sembra funzionare bene nel tradizionale importatore di CPython. Se hai un hook o un sistema di importazione speciale, non lo garantisco, ma se lo adotti, è abbastanza banale tornare indietro - dovrai solo aggiungere manualmente i nomi nel__all__

Quindi, ad esempio, in una libreria di utilità, dovresti definire il decoratore:

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

e quindi, dove definiresti un __all__, fai questo:

$ 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()

E questo funziona bene se eseguito come principale o importato da un'altra funzione.

$ cat > run.py
import main
main.main()

$ python run.py
main

E anche il provisioning API con import *funzionerà:

$ 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

1
Riferimento incrociato: ho citato il tuo decoratore in questa risposta CW alla domanda su come scrivere un @exportdecoratore.
MvG,

13
Questa è stata da sola la risposta più utile che ho visto per aiutare uno sviluppatore di pitone relativamente nuovo a comprendere il processo di importazione di moduli / pacchetti __init__.pye l'uso di__all__
Brett Reinhard,

Questo mi aiuta molto. Il mio problema, tuttavia, è che i sottomoduli che voglio importare sono tutti file generati con molta cruft nei loro simboli che vorrei eliminare, senza dover garantire manualmente che __all__sia corretto.
Mike C,

@MikeC allora forse dovresti generare __all__anche il tuo - ma poi direi che hai un'API instabile ... Questo sarebbe qualcosa su cui avere alcuni test di accettazione completi.
Aaron Hall

@AaronHall "non avranno tutti gli altri nomi ... ingombrando lo spazio dei nomi del pacchetto" Ma avranno i nomi module_1e module_2; va bene includere un esplicito del module_1in __init__.py? Sbaglio a pensare che valga la pena?
Mike C,

176

Sto solo aggiungendo questo per essere precisi:

Tutte le altre risposte si riferiscono ai moduli . La domanda originale menzionata esplicitamente __all__nei __init__.pyfile, quindi si tratta di pacchetti Python .

In genere, __all__entra in gioco solo quando viene utilizzata la from xxx import *variante importdell'istruzione. Questo vale sia per i pacchetti che per i moduli.

Il comportamento dei moduli è spiegato nelle altre risposte. L'esatto comportamento dei pacchetti è descritto qui in dettaglio.

In breve, __all__a livello di pacchetto fa all'incirca la stessa cosa dei moduli, tranne per il fatto che si occupa di moduli all'interno del pacchetto (al contrario di specificare i nomi all'interno del modulo ). __all__Specifica quindi tutti i moduli che devono essere caricati e importati nello spazio dei nomi corrente quando li utilizziamo from package import *.

La grande differenza è che quando si omette la dichiarazione di __all__in un pacchetto __init__.py, la dichiarazione from package import *non importerà nulla (con le eccezioni spiegate nella documentazione, vedere il link sopra).

D'altra parte, se si omette __all__in un modulo, l '"importazione speciale" importerà tutti i nomi (che non iniziano con un carattere di sottolineatura) definiti nel modulo.


29
from package import *importerà comunque tutto ciò che è stato definito __init__.py, anche se non è presente all. La differenza importante è che senza di __all__essa non verranno importati automaticamente i moduli definiti nella directory del pacchetto.
Nikratio,

Quando tutto contiene [foo, bar] e nel file test.py se utilizziamo: dal pacchetto import *, quindi, foo e bar vengono importati nello spazio dei nomi locale di test.py o nello spazio dei nomi foo e bar?
variabile

87

Cambia anche ciò che mostrerà Pydoc:

module1.py

a = "A"
b = "B"
c = "C"

module2.py

__all__ = ['a', 'b']

a = "A"
b = "B"
c = "C"

$ pydoc module1

Aiuto sul modulo module1:

NOME
    modulo 1

FILE
    module1.py

DATI 
    a = 'A'
     b = 'B'
     c = 'C'

$ pydoc module2

Aiuto sul modulo module2:

NOME
    Module2

FILE
    module2.py

DATI 
    __all__ = ['a', 'b']
     a = 'A'
     b = 'B'

Dichiaro __all__in tutti i miei moduli, oltre a sottolineare i dettagli interni, che sono di grande aiuto quando si usano cose che non si sono mai usate prima in sessioni di interpretazione dal vivo.


54

__all__personalizza *infrom <module> import *

__all__personalizza *infrom <package> import *


Un modulo è un .pyfile destinato ad essere importato.

Un pacchetto è una directory con un __init__.pyfile. Un pacchetto di solito contiene moduli.


MODULI

""" cheese.py - an example module """

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99

__all__fa conoscere agli umani le funzionalità "pubbliche" di un modulo . [ @AaronHall ] Inoltre, pydoc li riconosce. [ @Longpoke ]

dall'importazione del modulo *

Guarda come swisse cheddarvengono portati nello spazio dei nomi locale, ma non 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

Senza __all__, nessun simbolo (che non inizia con un trattino basso) sarebbe stato disponibile.


Le importazioni senza *non sono interessate da__all__


modulo di importazione

>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)

dai nomi di importazione del modulo

>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)

modulo di importazione come nome locale

>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)

PACCHI

Nel __init__.pyfile di un pacchetto __all__ è presente un elenco di stringhe con i nomi di moduli pubblici o altri oggetti. Queste funzionalità sono disponibili per l'importazione di caratteri jolly. Come per i moduli, __all__personalizza l' *importazione di caratteri jolly dal pacchetto. [ @MartinStettner ]

Ecco un estratto dal connettore MySQL Python __init__.py :

__all__ = [
    'MySQLConnection', 'Connect', 'custom_error_exception',

    # Some useful constants
    'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
    'HAVE_CEXT',

    # Error handling
    'Error', 'Warning',

    ...etc...

    ]

Il caso predefinito, l' asterisco senza __all__un pacchetto , è complicato, perché il comportamento ovvio sarebbe costoso: utilizzare il file system per cercare tutti i moduli nel pacchetto. Invece, nella mia lettura dei documenti, __init__.pyvengono importati solo gli oggetti definiti in :

Se __all__non è definito, la dichiarazione from sound.effects import *non non importa tutti i sottomoduli il pacchetto sound.effectsnello spazio dei nomi corrente; assicura solo che il pacchetto sound.effectssia stato importato (possibilmente eseguendo qualsiasi codice di inizializzazione in __init__.py) e quindi importa qualunque nome sia definito nel pacchetto. Ciò include tutti i nomi definiti (e sottomoduli caricati esplicitamente) da __init__.py. Include anche tutti i sottomoduli del pacchetto che sono stati esplicitamente caricati da precedenti dichiarazioni di importazione.


Le importazioni di caratteri jolly ... dovrebbero essere evitate in quanto [confondono] i lettori e molti strumenti automatici.

[ PEP 8 , @ToolmakerSteve]


2
Mi piace molto questa risposta, ma mi mancano le informazioni su quale sia il comportamento predefinito from <package> import *senza__all__ a __init__.pyche non importare qualsiasi modulo .
Radzak,

Grazie @Jatimir, ho chiarito il meglio che potevo senza fare esperimenti. Volevo quasi dire che questo caso (asterisco senza tutto per un pacchetto) si comporta come se __init__.pyfosse un modulo . Ma non sono sicuro che sia accurato, o in particolare se gli oggetti con prefisso di sottolineatura sono esclusi. Inoltre, ho separato più chiaramente le sezioni su MODULI e PACCHETTI. I vostri pensieri?
Bob Stein,

49

Da (An Unofficial) Python Reference Wiki :

I nomi pubblici definiti da un modulo sono determinati controllando lo spazio dei nomi del modulo per una variabile denominata __all__; se definito, deve essere una sequenza di stringhe che sono nomi definiti o importati da quel modulo. I nomi indicati __all__sono tutti considerati pubblici e devono esistere. Se __all__non è definito, l'insieme di nomi pubblici include tutti i nomi trovati nello spazio dei nomi del modulo che non iniziano con un carattere di sottolineatura ("_"). __all__dovrebbe contenere l'intera API pubblica. Ha lo scopo di evitare l'esportazione accidentale di elementi che non fanno parte dell'API (come i moduli di libreria che sono stati importati e utilizzati all'interno del modulo).


Il link elencato è morto. ma ho trovato il testo testualmente su vdocuments.net/… e qui: dokumen.tips/documents/reference-567bab8d6118a.html
JayRizzo

8

__all__è usato per documentare l'API pubblica di un modulo Python. Sebbene sia facoltativo,__all__ dovrebbe essere utilizzato.

Ecco l'estratto pertinente dal riferimento al linguaggio Python :

I nomi pubblici definiti da un modulo sono determinati controllando lo spazio dei nomi del modulo per una variabile denominata __all__; se definito, deve essere una sequenza di stringhe che sono nomi definiti o importati da quel modulo. I nomi indicati __all__sono tutti considerati pubblici e devono esistere. Se __all__non è definito, l'insieme di nomi pubblici include tutti i nomi trovati nello spazio dei nomi del modulo che non iniziano con un carattere di sottolineatura ('_'). __all__dovrebbe contenere l'intera API pubblica. Ha lo scopo di evitare l'esportazione accidentale di elementi che non fanno parte dell'API (come i moduli di libreria che sono stati importati e utilizzati all'interno del modulo).

PEP 8 utilizza una formulazione simile, sebbene chiarisca anche che i nomi importati non fanno parte dell'API pubblica quando __all__è assente:

Per supportare meglio l'introspezione, i moduli dovrebbero dichiarare esplicitamente i nomi nella loro API pubblica usando l' __all__attributo. Ambientazione__all__ su un elenco vuoto indica che il modulo non ha API pubbliche.

[...]

I nomi importati devono sempre essere considerati un dettaglio di implementazione. Altri moduli non devono fare affidamento sull'accesso indiretto a tali nomi importati a meno che non siano una parte esplicitamente documentata dell'API del modulo contenitore, come os.pathun __init__modulo del pacchetto o che espone funzionalità dai sottomoduli.

Inoltre, come sottolineato in altre risposte, __all__viene utilizzato per abilitare l' importazione di caratteri jolly per i pacchetti :

L'istruzione import utilizza la seguente convenzione: se il __init__.pycodice di un pacchetto definisce un elenco denominato __all__, viene considerato l'elenco dei nomi dei moduli che devono essere importati quando from package import *viene rilevato.


8

Risposta breve

__all__ colpisce from <module> import * dichiarazioni.

Risposta lunga

Considera questo esempio:

foo
├── bar.py
└── __init__.py

In foo/__init__.py:

  • (Implicito) Se non lo definiamo __all__, from foo import *importeremo solo i nomi definiti in foo/__init__.py.

  • (Esplicito) Se definiamo __all__ = [], quindi from foo import *non importerò nulla.

  • (Esplicito) Se lo definiamo __all__ = [ <name1>, ... ], from foo import *importeremo solo quei nomi.

Si noti che nel caso implicito, python non importerà i nomi che iniziano con _. Tuttavia, è possibile forzare l'importazione di tali nomi tramite__all__ .

Puoi visualizzare il documento Python qui .


5

__all__influenza come from foo import *funziona.

Il codice che si trova all'interno di un corpo del modulo (ma non nel corpo di una funzione o di una classe) può usare un asterisco ( *) in fromun'istruzione:

from foo import *

Le *richieste che tutti gli attributi del modulo foo(tranne quelli che iniziano con i trattini bassi) siano associati come variabili globali nel modulo di importazione. Quando fooha un attributo __all__, il valore dell'attributo è l'elenco dei nomi associati a questo tipo difrom istruzione.

Se fooè un pacchetto e __init__.pydefinisce un elenco denominato __all__, viene considerato l'elenco dei nomi dei sottomoduli che devono essere importati quando from foo import *viene rilevato. Se __all__non è definito, l'istruzione from foo import *importa tutti i nomi definiti nel pacchetto. Ciò include tutti i nomi definiti (e sottomoduli caricati esplicitamente) da __init__.py.

Nota che __all__non deve essere un elenco. Secondo la documentazione importsull'istruzione , se definita, __all__deve essere una sequenza di stringhe che sono nomi definiti o importati dal modulo. Quindi puoi anche usare una tupla per salvare alcuni cicli di memoria e CPU. Basta non dimenticare una virgola nel caso in cui il modulo definisca un singolo nome pubblico:

__all__ = ('some_name',)

Vedi anche Perché "import *" è male?


1

Questo è definito in PEP8 qui :

Nomi delle variabili globali

(Speriamo che queste variabili siano destinate all'uso all'interno di un solo modulo.) Le convenzioni sono quasi uguali a quelle per le funzioni.

I moduli progettati per essere utilizzati tramite from M import *dovrebbero utilizzare il __all__meccanismo per impedire l'esportazione di globi, oppure utilizzare la vecchia convenzione di prefisso tali globi con un carattere di sottolineatura (cosa che potresti voler fare per indicare che questi globi sono "moduli non pubblici").

PEP8 fornisce convenzioni di codifica per il codice Python che comprende la libreria standard nella distribuzione principale di Python. Più segui questo, più sei vicino all'intento originale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.