Domanda

Utilizzo Python sempre di più e continuo a vedere la variabile __all__ impostato in modo diverso __init__.py File.Qualcuno può spiegare cosa fa?

È stato utile?

Soluzione

È 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.

Altri suggerimenti

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

Ad esempio, il codice seguente in a foo.py esporta esplicitamente i simboli bar E 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 la __all__ sopra è commentato, questo codice verrà quindi eseguito fino al completamento, come comportamento predefinito di import * consiste nell'importare tutti i simboli che non iniziano con un carattere di sottolineatura, dal namespace specificato.

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

NOTA: __all__ influenza il from <module> import * solo comportamento.Membri che non sono menzionati in __all__ sono ancora accessibili dall'esterno del modulo e possono essere importati con from <module> import <member>.

Aggiungo solo questo per essere precisi:

Tutte le altre risposte si riferiscono a moduli.La domanda originale è menzionata esplicitamente __all__ In __init__.py file, quindi si tratta di Python Pacchetti.

Generalmente, __all__ entra in gioco solo quando il from xxx import * variante del import viene utilizzata l'istruzione.Questo vale sia per i pacchetti che per i moduli.

Il comportamento dei moduli è spiegato nelle altre risposte.Viene descritto il comportamento esatto dei pacchetti Qui in dettaglio.

In breve, __all__ a livello di pacchetto fa più o meno la stessa cosa che per i moduli, tranne che si occupa di moduli all'interno del pacchetto (a differenza di specificare nomi all'interno del modulo).COSÌ __all__ specifica tutti i moduli che verranno caricati e importati nello spazio dei nomi corrente quando li utilizziamo from package import *.

La grande differenza è che quando tu omettere 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 collegamento sopra).

D'altra parte, se ometti __all__ in un modulo, l'"importazione con asterisco" importerà tutti i nomi (che non iniziano con un carattere di sottolineatura) definiti nel modulo.

Spiegare __tutto__ in Python?

Continuo a vedere la variabile __all__ impostato in modo diverso __init__.py File.

Cosa fa questo?

Cosa fa __all__ Fare?

Dichiara i nomi semanticamente "pubblici" da un modulo.Se c'è un nome in __all__, ci si aspetta che gli utenti lo utilizzino e possono avere l'aspettativa che non cambierà.

Avrà anche effetti programmatici:

import *

__all__ in un modulo, ad es. module.py:

__all__ = ['foo', 'Bar']

significa che quando tu import * dal modulo, solo i nomi nel file __all__ vengono importati:

from module import *               # imports foo and Bar

Strumenti di documentazione

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

__init__.py rende una directory un pacchetto Python

Dal documenti:

IL __init__.py i file sono necessari per fare in modo che Python tratti le directory come contenenti pacchetti;questo viene fatto per evitare che le directory con un nome comune, come string, nascondano involontariamente moduli validi che si verificano successivamente nel percorso di ricerca dei moduli.

Nel caso più semplice, __init__.py può essere semplicemente un file vuoto, ma può anche eseguire il codice di inizializzazione per il pacchetto o impostare il file __all__ variabile.

Così il __init__.py può dichiarare il __all__ per un pacchetto.

Gestire un'API:

Un pacchetto è tipicamente composto da moduli che possono importarsi a vicenda, ma che sono necessariamente legati insieme da un file __init__.py file.Quel file è ciò che rende la directory un vero pacchetto Python.Ad esempio, supponiamo di avere quanto segue:

 package/
   |-__init__.py # makes directory a Python package
   |-module_1.py
   |-module_2.py

nel __init__.py Scrivi:

from module_1 import *
from module_2 import *

e dentro module_1 hai:

__all__ = ['foo',]

e dentro module_2 hai:

__all__ = ['Bar',]

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

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

E non avranno tutti gli altri nomi che hai usato durante la creazione dei moduli che ingombrano il file package spazio dei nomi.

__all__ In __init__.py

Dopo più lavoro, forse hai deciso che i moduli sono troppo grandi e devono essere divisi.Quindi fai quanto segue:

 package/
   |-__init__.py
   |-module_1/
   |  |-__init__.py
   |  |-foo_implementation.py
   |-module_2/
      |-__init__.py
      |-Bar_implementation.py

E in ciascuno __init__.py dichiari un __all__, per esempio.nel modulo_1:

from foo_implementation import *
__all__ = ['foo']

E i moduli_2 __init__.py:

from Bar_implementation import *
__all__ = ['Bar']

E puoi facilmente aggiungere elementi alla tua API che puoi gestire a livello di sottopacchetto anziché a livello di modulo del sottopacchetto.Se desideri aggiungere un nuovo nome all'API, aggiorna semplicemente il file __init__.py, per esempio.nel modulo_2:

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

E se non sei pronto per pubblicare Baz nell'API di livello superiore, nel tuo livello superiore __init__.py potresti 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 sanno, altri strumenti (come pydoc) non li informerà.

Potrai modificarlo in seguito 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 an _.Tu certamente Potevo affidarsi a questo meccanismo.Alcuni pacchetti nella libreria standard Python, infatti, Fare fanno affidamento su questo, ma per farlo, trasformano le loro importazioni, ad esempio, in ctypes/__init__.py:

import os as _os, sys as _sys

Usando il _ La convenzione può essere più elegante perché rimuove la ridondanza di nominare nuovamente i nomi.Ma aggiunge la ridondanza per le importazioni (se ne hai molte) e così è facile dimenticare di farlo in modo coerente - e l'ultima cosa che vuoi è dover supportare indefinitamente qualcosa che volevi fosse solo un dettaglio di implementazione, solo perché hai dimenticato di prefissare un _ quando si nomina una funzione.

Personalmente scrivo un __all__ all'inizio del mio ciclo di vita di sviluppo dei moduli in modo che altri che potrebbero utilizzare il mio codice sappiano cosa dovrebbero usare e cosa non usare.

Utilizzano anche la maggior parte dei pacchetti nella libreria standard __all__.

Quando si evita __all__ ha senso

Ha senso attenersi al _ convenzione del prefisso al posto di __all__ Quando:

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

UN export decoratore

Lo svantaggio dell'utilizzo __all__ è che devi scrivere due volte i nomi delle funzioni e delle classi da esportare e le informazioni vengono mantenute separate dalle definizioni.Noi Potevo utilizzare un decoratore per risolvere questo problema.

L'idea di questo decoratore da esportazione mi è venuta dal discorso di David Beazley sul packaging.Questa implementazione sembra funzionare bene nell'importatore tradizionale di CPython.Se hai un hook o un sistema di importazione speciale, non lo garantisco, ma se lo adotti, è abbastanza banale fare marcia indietro: dovrai solo aggiungere nuovamente manualmente i nomi nel file __all__

Quindi, ad esempio, in una libreria di utilità, definiresti 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 poi, dove definiresti an __all__, tu lo fai:

$ 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 funziona bene sia che venga eseguito come principale o importato da un'altra funzione.

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

$ python run.py
main

E provisioning API con import * funzionerà anche:

$ 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

Cambia anche ciò che pydoc mostrerà:

modulo1.py

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

modulo2.py

__all__ = ['a', 'b']

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

$ modulo pydoc1

Help on module module1:

NOME
    module1

FILE
    module1.py

DATI
    UN = 'A'
    B = 'B'
    C = 'C'

$ modulo pydoc2

Help on module module2:

NOME
    module2

FILE
    module2.py

DATI
    __Tutto__ = ['a', 'b']
    UN = 'A'
    B = 'B'

dichiaro __all__ in tutti i miei moduli, oltre a sottolineare i dettagli interni, questi aiutano davvero quando usi cose che non hai mai usato prima nelle sessioni di interprete dal vivo.

Da Wiki di riferimento Python (non ufficiale).:

I nomi pubblici definiti da un modulo vengono 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 riportati __all__ sono tutti considerati pubblici e devono esistere.Se __all__ non è definito, l'insieme dei 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.Lo scopo è evitare l'esportazione accidentale di elementi che non fanno parte dell'API (come i moduli di libreria importati e utilizzati all'interno del modulo).

__all__ personalizza l'asterisco In from <module> import *

__all__ personalizza l'asterisco In from <package> import *


UN modulo è un .py file destinato a essere importato.

UN pacchetto è una directory con a __init__.py file.Un pacchetto solitamente contiene moduli.


MODULI

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

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99

__all__ consente agli esseri umani di conoscere le caratteristiche "pubbliche" di a modulo.[@AaronHall] Inoltre, Pydoc li riconosce.[@Longpoke]

da modulo importa *

Vedi come swiss E cheddar vengono 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__, qualsiasi simbolo (che non inizia con un carattere di sottolineatura) sarebbe stato disponibile.


Importazioni senza * non sono influenzati da __all__


importare modulo

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

da modulo importare nomi

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

importare modulo COME nomelocale

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

PACCHETTI

Nel __init__.py file di a pacchetto __all__ è un elenco di stringhe con i nomi di moduli pubblici o altri oggetti.Queste funzionalità sono disponibili per le importazioni di caratteri jolly.Come per i moduli, __all__ personalizza il * durante 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, asterisco con n __all__ per un pacchetto, è complicato, perché il comportamento ovvio sarebbe costoso:per utilizzare il file system per cercare tutti i moduli nel pacchetto.Invece, nella mia lettura dei documenti, solo gli oggetti definiti in __init__.py vengono importati:

Se __all__ non è definita, la dichiarazione from sound.effects import * fa non importare tutti i sottomoduli dal pacchetto sound.effects nello spazio dei nomi corrente;garantisce solo che il pacchetto sound.effects è stato importato (possibilmente eseguendo qualsiasi codice di inizializzazione in __init__.py) e quindi importa tutti i nomi definiti nel pacchetto.Ciò include tutti i nomi definiti (e i sottomoduli caricati esplicitamente) da __init__.py.Include anche eventuali sottomoduli del pacchetto caricati esplicitamente dalle precedenti istruzioni di importazione.


Importazioni di caratteri jolly...dovrebbero essere evitati poiché [confondono] i lettori e molti strumenti automatizzati.

[PEP 8, @ToolmakerSteve]

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 definiamo __all__, Poi from foo import * importerà solo i nomi definiti in foo/__init__.py.

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

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

Tieni presente che nel caso implicito, Python non importerà i nomi che iniziano con _.Tuttavia, puoi forzare l'importazione di tali nomi utilizzando __all__.

È possibile visualizzare il documento Python Qui.

__all__ viene utilizzato per documentare l'API pubblica di un modulo Python.Anche se è facoltativo, __all__ dovrebbe essere usato.

Ecco l'estratto pertinente da il riferimento al linguaggio Python:

I nomi pubblici definiti da un modulo vengono 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 riportati __all__ sono tutti considerati pubblici e devono esistere.Se __all__ non è definito, l'insieme dei 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.Lo scopo è evitare l'esportazione accidentale di elementi che non fanno parte dell'API (come i moduli di libreria 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 utilizzando il file __all__ attributo.Collocamento __all__ su un elenco vuoto indica che il modulo non ha API pubbliche.

[...]

I nomi importati dovrebbero 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 che li contiene, come ad esempio os.path o un pacchetto __init__ modulo che espone funzionalità dai sottomoduli.

Inoltre, come sottolineato in altre risposte, __all__ viene utilizzato per abilitare importazione di caratteri jolly per i pacchetti:

L'istruzione import utilizza la seguente convenzione:se un pacchetto è __init__.py il codice definisce un elenco denominato __all__, viene considerato l'elenco dei nomi dei moduli che dovrebbero essere importati quando from package import * si incontra.

Oltre alle risposte esistenti, __all__ non deve essere un elenco.Come da documentazione sul import dichiarazione, se definito, __all__ deve essere un sequenza di stringhe che sono nomi definiti o importati dal modulo.Quindi potresti anche usare una tupla per salva 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',)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top