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?

¿Fue útil?

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ón from sound.effects import * hace no importar todos los submódulos del paquete sound.effects en el espacio de nombres actual;sólo garantiza que el paquete sound.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__, entonces from foo import * sólo importará nombres definidos en foo/__init__.py.

  • (Explícito) Si definimos __all__ = [], entonces from foo import * no importará nada.

  • (Explícito) Si definimos __all__ = [ <name1>, ... ], entonces from 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 cuando from 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',)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top