¿Alguien puede explicar __all__ en Python?
-
09-06-2019 - |
Pregunta
He estado usando Python cada vez más y sigo viendo la variable __all__
ambientado en diferentes __init__.py
archivos.¿Alguien puede explicar qué hace esto?
Solución
Es una lista de objetos públicos de ese módulo, según lo interpretado por import *
.Anula el valor predeterminado de ocultar todo lo que comienza con un guión bajo.
Otros consejos
Vinculado a, pero no mencionado explícitamente aquí, es exactamente cuando __all__
se utiliza.Es una lista de cadenas que definen qué símbolos en un módulo se exportarán cuando from <module> import *
se utiliza en el módulo.
Por ejemplo, el siguiente código en un foo.py
exporta explícitamente los símbolos bar
y baz
:
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): return 'baz'
Luego, estos símbolos se pueden importar así:
from foo import *
print(bar)
print(baz)
# The following will trigger an exception, as "waz" is not exported by the module
print(waz)
Si el __all__
lo anterior está comentado, este código se ejecutará hasta su finalización, como comportamiento predeterminado de import *
es importar todos los símbolos que no comienzan con un guión bajo, desde el espacio de nombres dado.
Referencia: https://docs.python.org/tutorial/modules.html#importar-desde-un-paquete
NOTA: __all__
afecta el from <module> import *
comportamiento únicamente.Miembros que no están mencionados en __all__
todavía son accesibles desde fuera del módulo y se pueden importar con from <module> import <member>
.
Solo agrego esto para ser preciso:
Todas las demás respuestas se refieren a módulos.La pregunta original menciona explícitamente __all__
en __init__.py
archivos, entonces esto es sobre Python paquetes.
Generalmente, __all__
sólo entra en juego cuando el from xxx import *
variante de la import
Se utiliza la declaración.Esto se aplica tanto a paquetes como a módulos.
El comportamiento de los módulos se explica en las otras respuestas.Se describe el comportamiento exacto de los paquetes. aquí en detalle.
En breve, __all__
a nivel de paquete hace aproximadamente lo mismo que para los módulos, excepto que se ocupa de módulos dentro del paquete (en contraste con especificar nombres dentro del módulo).Entonces __all__
especifica todos los módulos que se cargarán e importarán al espacio de nombres actual cuando usemos from package import *
.
La gran diferencia es que cuando omitir la declaración de __all__
en un paquete __init__.py
, la declaración from package import *
no importará nada en absoluto (con las excepciones explicadas en la documentación, consulte el enlace anterior).
Por otro lado, si omites __all__
en un módulo, la "importación destacada" importará todos los nombres (que no comiencen con un guión bajo) definidos en el módulo.
¿Explicar __todo__ en Python?
sigo viendo la variable
__all__
ambientado en diferentes__init__.py
archivos.¿Qué hace esto?
Que hace __all__
¿hacer?
Declara los nombres semánticamente "públicos" de un módulo.Si hay un nombre en __all__
, se espera que los usuarios lo utilicen y pueden tener la expectativa de que no cambiará.
También tendrá efectos programáticos:
import *
__all__
en un módulo, p.e. module.py
:
__all__ = ['foo', 'Bar']
significa que cuando tu import *
del módulo, sólo aquellos nombres en el __all__
son importados:
from module import * # imports foo and Bar
Herramientas de documentación
Las herramientas de autocompletado de código y documentación también pueden (de hecho, deberían) inspeccionar el __all__
para determinar qué nombres mostrar como disponibles en un módulo.
__init__.py
convierte un directorio en un paquete de Python
Desde el documentos:
El
__init__.py
se requieren archivos para que Python trate los directorios como si contuvieran paquetes;esto se hace para evitar que los directorios con un nombre común, como una cadena, oculten involuntariamente módulos válidos que aparecen más adelante en la ruta de búsqueda del módulo.En el caso más simple,
__init__.py
puede ser simplemente un archivo vacío, pero también puede ejecutar el código de inicialización para el paquete o configurar el__all__
variable.
Entonces el __init__.py
puede declarar el __all__
para paquete.
Administrar una API:
Un paquete normalmente se compone de módulos que pueden importarse entre sí, pero que necesariamente están unidos con un __init__.py
archivo.Ese archivo es lo que hace que el directorio sea un paquete Python real.Por ejemplo, digamos que tiene lo siguiente:
package/
|-__init__.py # makes directory a Python package
|-module_1.py
|-module_2.py
en el __init__.py
usted escribe:
from module_1 import *
from module_2 import *
y en module_1
tienes:
__all__ = ['foo',]
y en module_2
tienes:
__all__ = ['Bar',]
Y ahora ha presentado una API completa que otra persona puede usar cuando importa su paquete, así:
import package
package.foo()
package.Bar()
Y no tendrán todos los otros nombres que usaste al crear tus módulos saturando el package
espacio de nombres.
__all__
en __init__.py
Después de trabajar más, tal vez hayas decidido que los módulos son demasiado grandes y es necesario dividirlos.Entonces haces lo siguiente:
package/
|-__init__.py
|-module_1/
| |-__init__.py
| |-foo_implementation.py
|-module_2/
|-__init__.py
|-Bar_implementation.py
y en cada uno __init__.py
declaras un __all__
, p.ej.en módulo_1:
from foo_implementation import *
__all__ = ['foo']
Y el módulo_2 __init__.py
:
from Bar_implementation import *
__all__ = ['Bar']
Y puede agregar fácilmente cosas a su API que puede administrar en el nivel de subpaquete en lugar del nivel de módulo del subpaquete.Si desea agregar un nuevo nombre a la API, simplemente actualice el __init__.py
, p.ej.en módulo_2:
from Bar_implementation import *
from Baz_implementation import *
__all__ = ['Bar', 'Baz']
Y si no estás listo para publicar Baz
en la API de nivel superior, en su nivel superior __init__.py
podrías tener:
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
y si sus usuarios conocen la disponibilidad de Baz
, pueden usarlo:
import package
package.Baz()
pero si no lo saben, otras herramientas (como pydoc) no les informará.
Luego puedes cambiar eso cuando Baz
está listo para el horario de máxima audiencia:
from module_1 import *
from module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
Prefijo _
versus __all__
:
De forma predeterminada, Python exportará todos los nombres que no comiencen con un _
.ciertamente tu podría confiar en este mecanismo.Algunos paquetes en la biblioteca estándar de Python, de hecho, hacer dependen de esto, pero para hacerlo, asignan alias a sus importaciones, por ejemplo, en ctypes/__init__.py
:
import os as _os, sys as _sys
Utilizando el _
La convención puede ser más elegante porque elimina la redundancia de nombrar los nombres nuevamente.Pero añade la redundancia para las importaciones (si tienes muchas) y es fácil olvidarse de hacer esto de manera consistente, y lo último que desea es tener que soportar indefinidamente algo que pretendía que fuera solo un detalle de implementación, solo porque olvidó anteponer un _
al nombrar una función.
Yo personalmente escribo un __all__
temprano en mi ciclo de vida de desarrollo de módulos para que otros que puedan usar mi código sepan qué deben usar y qué no.
La mayoría de los paquetes de la biblioteca estándar también utilizan __all__
.
Al evitar __all__
tiene sentido
Tiene sentido ceñirse a la _
convención de prefijo en lugar de __all__
cuando:
- Todavía estás en el modo de desarrollo inicial, no tienes usuarios y estás modificando constantemente tu API.
- Tal vez tenga usuarios, pero tiene pruebas unitarias que cubren la API y todavía está agregando activamente la API y modificando el desarrollo.
Un export
decorador
La desventaja de usar __all__
es que debe escribir los nombres de las funciones y clases que se exportan dos veces, y la información se mantiene separada de las definiciones.Nosotros podría Utilice un decorador para resolver este problema.
Se me ocurrió la idea de un decorador de exportación de la charla de David Beazley sobre embalaje.Esta implementación parece funcionar bien en el importador tradicional de CPython.Si tiene un gancho o sistema de importación especial, no lo garantizo, pero si lo adopta, es bastante trivial retirarse: solo necesitará volver a agregar manualmente los nombres al archivo __all__
Entonces, por ejemplo, en una biblioteca de utilidades, definirías el decorador:
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
y luego, donde definirías un __all__
, tu hiciste esto:
$ 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()
Y esto funciona bien ya sea que se ejecute como principal o se importe mediante otra función.
$ cat > run.py
import main
main.main()
$ python run.py
main
Y aprovisionamiento de API con import *
funcionará también:
$ 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
También cambia lo que mostrará pydoc:
módulo1.py
a = "A"
b = "B"
c = "C"
módulo2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$ módulo pydoc1
Help on module module1: NOMBRE module1 ARCHIVO module1.py DATOS a = 'A' b = 'B' C = 'C'
$ pydoc módulo2
Help on module module2: NOMBRE module2 ARCHIVO module2.py DATOS __todo__ = ['a', 'b'] a = 'A' b = 'B'
declaro __all__
En todos mis módulos, además de resaltar los detalles internos, estos realmente ayudan cuando se usan cosas que nunca antes has usado en sesiones de intérprete en vivo.
De (Un Wiki de referencia de Python no oficial):
Los nombres públicos definidos por un módulo se determinan comprobando el espacio de nombres del módulo en busca de una variable denominada
__all__
;si está definido, debe ser una secuencia de cadenas que sean nombres definidos o importados por ese módulo.Los nombres dados en__all__
Todos se consideran públicos y deben existir.Si__all__
no está definido, el conjunto de nombres públicos incluye todos los nombres encontrados en el espacio de nombres del módulo que no comienzan con un carácter de subrayado ("_").__all__
debe contener toda la API pública.Su objetivo es evitar la exportación accidental de elementos que no forman parte de la API (como módulos de biblioteca que se importaron y utilizaron dentro del módulo).
__all__
personaliza el asterisco en from <module> import *
__all__
personaliza el asterisco en from <package> import *
A módulo es un .py
archivo destinado a ser importado.
A paquete es un directorio con un __init__.py
archivo.Un paquete normalmente contiene módulos.
MÓDULOS
""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99
cheddar = 3.99
gouda = 10.99
__all__
permite a los humanos conocer las características "públicas" de un módulo.[@AaronHall] Además, pydoc los reconoce.[@Longpoke]
de módulo importar *
Ver cómo swiss
y cheddar
se llevan al espacio de nombres local, pero no 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
Sin __all__
, cualquier símbolo (que no comience con un guión bajo) habría estado disponible.
Importaciones sin *
no se ven afectados por __all__
importar módulo
>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)
de módulo importar nombres
>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)
importar módulo como nombre local
>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)
PAQUETES
En el __init__.py
archivo de un paquete __all__
es una lista de cadenas con los nombres de módulos públicos u otros objetos.Esas funciones están disponibles para importaciones con comodines.Al igual que con los módulos, __all__
personaliza el *
al importar comodines desde el paquete.[@MartinStettner]
He aquí un extracto del Conector Python MySQL __init__.py
:
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
El caso predeterminado, asterisco sin __all__
por un paquete, es complicado, porque el comportamiento obvio sería costoso:utilizar el sistema de archivos para buscar todos los módulos del paquete.En cambio, en mi lectura de los documentos, solo los objetos definidos en __init__.py
son importados:
Si
__all__
no está definido, la declaraciónfrom sound.effects import *
hace no importar todos los submódulos del paquetesound.effects
en el espacio de nombres actual;sólo garantiza que el paquetesound.effects
ha sido importado (posiblemente ejecutando cualquier código de inicialización en__init__.py
) y luego importa los nombres definidos en el paquete.Esto incluye cualquier nombre definido (y submódulos cargados explícitamente) por__init__.py
.También incluye cualquier submódulo del paquete que se haya cargado explícitamente mediante declaraciones de importación anteriores.
Importaciones comodín...deben evitarse ya que [confunden] a los lectores y a muchas herramientas automatizadas.
[PEPE 8, @ToolmakerSteve]
Respuesta corta
__all__
afecta from <module> import *
declaraciones.
Respuesta larga
Considere este ejemplo:
foo
├── bar.py
└── __init__.py
En foo/__init__.py
:
(Implícito) Si no definimos
__all__
, entoncesfrom foo import *
sólo importará nombres definidos enfoo/__init__.py
.(Explícito) Si definimos
__all__ = []
, entoncesfrom foo import *
no importará nada.(Explícito) Si definimos
__all__ = [ <name1>, ... ]
, entoncesfrom foo import *
sólo importará esos nombres.
Tenga en cuenta que en el caso implícito, Python no importará nombres que comiencen con _
.Sin embargo, puede forzar la importación de dichos nombres usando __all__
.
Puedes ver el documento de Python. aquí.
__all__
se utiliza para documentar la API pública de un módulo de Python.Aunque es opcional, __all__
debería ser usado.
Aquí está el extracto relevante de la referencia del lenguaje Python:
Los nombres públicos definidos por un módulo se determinan comprobando el espacio de nombres del módulo en busca de una variable denominada
__all__
;si está definido, debe ser una secuencia de cadenas que sean nombres definidos o importados por ese módulo.Los nombres dados en__all__
Todos se consideran públicos y deben existir.Si__all__
no está definido, el conjunto de nombres públicos incluye todos los nombres encontrados en el espacio de nombres del módulo que no comienzan con un carácter de subrayado ('_').__all__
debe contener toda la API pública.Su objetivo es evitar la exportación accidental de elementos que no forman parte de la API (como módulos de biblioteca que se importaron y utilizaron dentro del módulo).
PEPE 8 utiliza una redacción similar, aunque también deja claro que los nombres importados no forman parte de la API pública cuando __all__
está ausente:
Para admitir mejor la introspección, los módulos deben declarar explícitamente los nombres en su API pública utilizando el
__all__
atributo.Configuración__all__
a una lista vacía indica que el módulo no tiene API pública.[...]
Los nombres importados siempre deben considerarse un detalle de implementación.Otros módulos no deben depender del acceso indirecto a dichos nombres importados a menos que sean una parte explícitamente documentada de la API del módulo que los contiene, como
os.path
o un paquete__init__
Módulo que expone la funcionalidad de los submódulos.
Además, como se señaló en otras respuestas, __all__
se utiliza para habilitar importación de comodines para paquetes:
La declaración de importación utiliza la siguiente convención:si es un paquete
__init__.py
El código define una lista llamada__all__
, se considera la lista de nombres de módulos que deben importarse cuandofrom package import *
se encuentra.
Además de las respuestas existentes, __all__
No tiene por qué ser una lista.Según la documentación del import
declaración, si se define, __all__
debe ser un secuencia de cuerdas que son nombres definidos o importados por el módulo.Entonces también puedes usar una tupla para ahorrar algunos ciclos de memoria y CPU.Simplemente no olvide una coma en caso de que el módulo defina un único nombre público:
__all__ = ('some_name',)