¿Alguien puede explicar __todos__ en Python?


983

He estado usando Python cada vez más, y sigo viendo la variable __all__establecida en diferentes __init__.pyarchivos. ¿Alguien puede explicar qué hace esto?

Respuestas:


566

Es una lista de objetos públicos de ese módulo, según la interpretación de import *. Anula el valor predeterminado de ocultar todo lo que comienza con un guión bajo.


146
Los objetos que comienzan con un guión bajo, o no se mencionan en __all__si __all__está presente, no están exactamente ocultos; pueden verse y accederse perfectamente normalmente si conoce sus nombres. Solo en el caso de una "importación *", que de todos modos no se recomienda, la distinción tiene algún peso.
Brandon Rhodes

28
@BrandonRhodes: eso tampoco es exactamente cierto: se recomienda importar solo los módulos para los que sabe que están diseñados import *(como, por ejemplo tk). Una buena pista si este es el caso es la presencia de __all__o nombres que comienzan con un guión bajo en el código del módulo.
ovejas voladoras

12
Interfaces públicas e internas: python.org/dev/peps/pep-0008/#id50 . Para admitir mejor la introspección, los módulos deben declarar explícitamente los nombres en su API pública utilizando el atributo __all__. Establecer __todos__ en una lista vacía indica que el módulo no tiene una API pública.
depurar el

No estoy seguro de que si tkse publicaran hoy (o incluso en 2012), la práctica recomendada sería usar from tk import *. Creo que la práctica se acepta por inercia, no por diseño intencional.
chepner el

Como BrandonRhodes señala, esto realmente no es correcto
duhaime

947

Vinculado, pero no mencionado explícitamente aquí, es exactamente cuando __all__se usa. Es una lista de cadenas que definen qué símbolos en un módulo se exportarán cuando from <module> import *se usen en el módulo.

Por ejemplo, el siguiente código de forma foo.pyexplícita exporta los símbolos bary baz:

__all__ = ['bar', 'baz']

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

Estos símbolos pueden importarse 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 __all__se comenta lo anterior, este código se ejecutará hasta su finalización, ya que el 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#importing-from-a-package

NOTA: solo __all__ afecta el from <module> import *comportamiento. Los miembros que no se mencionan __all__aún son accesibles desde fuera del módulo y se pueden importar con from <module> import <member>.


1
¿No deberíamos imprimir baz como print(baz())?
John Cole

@JohnCole baz es objeto de función y baz () ejecutará el objeto de función
Bhanu Tez

@BhanuTez exactamente. Entonces print(baz)imprime algo parecido a <function baz at 0x7f32bc363c10>mientras que print(baz())imprimebaz
John Cole

224

¿Explicar __todos__ en Python?

Sigo viendo la variable __all__establecida en diferentes __init__.pyarchivos.

¿Qué hace esto?

¿Qué __all__hacer?

Declara los nombres semánticamente "públicos" de un módulo. Si hay un nombre __all__, se espera que los usuarios lo usen, y pueden esperar que no cambie.

También tendrá efectos programáticos:

import *

__all__en un módulo, por ejemplo module.py:

__all__ = ['foo', 'Bar']

significa que, import *desde el módulo, solo se importan los nombres en el __all__:

from module import *               # imports foo and Bar

Herramientas de documentación

Las herramientas de autocompletado de código y documentación pueden (de hecho, deberían) también inspeccionar __all__para determinar qué nombres mostrar como disponibles desde un módulo.

__init__.py convierte un directorio en un paquete de Python

De los documentos :

Los __init__.pyarchivos son necesarios para que Python trate los directorios como paquetes; Esto se hace para evitar que los directorios con un nombre común, como cadena, oculten involuntariamente módulos válidos que se producen más adelante en la ruta de búsqueda del módulo.

En el caso más simple, __init__.pypuede ser un archivo vacío, pero también puede ejecutar el código de inicialización del paquete o establecer el__all__ variable.

Entonces __init__.pypuede declarar el __all__para un paquete .

Administrar una API:

Un paquete generalmente está compuesto por módulos que pueden importarse entre sí, pero que necesariamente están vinculados con un __init__.pyarchivo. Ese archivo es lo que hace que el directorio sea un paquete de Python real. Por ejemplo, supongamos que tiene los siguientes archivos en un paquete:

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

Creemos estos archivos con Python para que pueda seguirlos: puede pegar lo siguiente en un shell de 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
""")

Y ahora ha presentado una API completa que otra persona puede usar cuando importan su paquete, así:

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

Y el paquete no tendrá todos los demás detalles de implementación que usó al crear sus módulos abarrotando el package espacio de nombres.

__all__ en __init__.py

Después de más trabajo, tal vez haya decidido que los módulos son demasiado grandes (¿como muchos miles de líneas?) Y deben dividirse. Entonces haces lo siguiente:

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

Primero haga los directorios de subpaquete con los mismos nombres que los módulos:

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

Mover las implementaciones:

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

cree __init__.pys para los subpaquetes que declaran el __all__para cada:

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

Y ahora todavía tiene la API aprovisionada a nivel de paquete:

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

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 __init__.py, por ejemplo, en module_2:

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

Y si no está listo para publicar Bazen la API de nivel superior, en su nivel superior __init__.pypodría 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 usarla:

import package
package.Baz()

pero si no lo saben, otras herramientas (como pydoc ) no les informarán.

Más tarde puede cambiar eso cuando Bazesté listo para el horario estelar:

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

Prefijo _versus__all__ :

Por defecto, Python exportará todos los nombres que no comiencen con un _. Ciertamente podría confiar en este mecanismo. Algunos paquetes de la biblioteca estándar de Python, de hecho, no se basan en esto, pero para hacerlo, ellos alias de sus importaciones, por ejemplo, en ctypes/__init__.py:

import os as _os, sys as _sys

Usar la _convención puede ser más elegante porque elimina la redundancia de nombrar los nombres nuevamente. Pero agrega la redundancia para las importaciones (si tiene muchas) y es fácil olvidarse de hacer esto constantemente, y lo último que desea es tener que respaldar indefinidamente algo que pretendía ser solo un detalle de implementación, solo porque olvidó prefijar un _al nombrar una función.

Personalmente, escribo una de las __all__primeras etapas de mi ciclo de vida de desarrollo para módulos para que otras personas que puedan usar mi código sepan qué deben usar y qué no.

La mayoría de los paquetes en la biblioteca estándar también usan __all__.

Cuando evitar __all__tiene sentido

Tiene sentido apegarse a la _convención de prefijo en lugar de __all__cuando:

  • Todavía está en modo de desarrollo temprano y no tiene usuarios, y está constantemente modificando su API.
  • Tal vez tenga usuarios, pero tiene pruebas unitarias que cubren la API, y todavía está agregando activamente a la API y ajustando en el desarrollo.

Un export decorador

La desventaja de usar __all__es que tiene que escribir los nombres de las funciones y clases que se exportan dos veces, y la información se mantiene separada de las definiciones. nos podía usar un decorador para resolver este problema.

La idea de un decorador de exportación de este tipo surgió de la charla de David Beazley sobre el 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 tendrá que agregar manualmente los nombres nuevamente en el__all__

Entonces, por ejemplo, en una biblioteca de utilidades, definiría 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__, haces 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 si se ejecuta como principal o importado por otra función.

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

$ python run.py
main

Y el aprovisionamiento de API import *también funcionará:

$ 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
Referencia cruzada: mencioné su decorador en esta respuesta de CW a la pregunta de cómo escribir un @exportdecorador.
MvG

13
Esta ha sido la respuesta más útil que he visto con respecto a ayudar a un desarrollador de Python relativamente nuevo a comprender el proceso de importación de módulos / paquetes __init__.pyy el uso de__all__
Brett Reinhard el

Esto me ayuda mucho. Mi problema, sin embargo, es que los submódulos que quiero importar son todos archivos generados con una gran cantidad de cruft en sus símbolos que me gustaría eliminar, sin tener que asegurarme manualmente de que __all__es correcto.
Mike C

@MikeC, entonces tal vez deberías generar tu __all__también, pero diría que tienes una API inestable ... Esto sería algo para tener algunas pruebas de aceptación exhaustivas.
Aaron Hall

@AaronHall "no tendrán todos los otros nombres ... abarrotando el espacio de nombres del paquete" Pero tendrán los nombres module_1y module_2; ¿Está bien incluir un explícito del module_1en __init__.py? ¿Me equivoco al pensar que esto vale la pena?
Mike C

176

Solo estoy agregando esto para ser precisos:

Todas las demás respuestas se refieren a módulos . La pregunta original mencionada explícitamente __all__en los __init__.pyarchivos, por lo que se trata de paquetes de Python .

En general, __all__solo entra en juego cuando se utiliza la from xxx import *variante de la importdeclaración. Esto se aplica tanto a los paquetes como a los módulos.

El comportamiento de los módulos se explica en las otras respuestas. El comportamiento exacto de los paquetes se describe aquí en detalle.

En resumen, __all__a nivel de paquete hace aproximadamente lo mismo que para los módulos, excepto que trata con módulos dentro del paquete (en contraste con la especificación de nombres dentro del módulo ). Por lo tanto, __all__especifica todos los módulos que se cargarán e importarán en el espacio de nombres actual cuando lo usemos from package import *.

La gran diferencia es que cuando omite la declaración de __all__un paquete __init__.py, la declaración from package import *no importará nada (con las excepciones explicadas en la documentación, consulte el enlace anterior).

Por otro lado, si omite __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.


29
from package import *aún importará todo lo definido en __init__.py, incluso si no hay all. La diferencia importante es que sin __all__él no importará automáticamente ningún módulo definido en el directorio del paquete.
Nikratio

Cuando todo contiene [foo, bar] y en el archivo test.py si usamos: desde la importación del paquete *, ¿se importan foo y bar en el espacio de nombres local de test.py o en el propio espacio de nombres foo and bars?
variable

87

También cambia lo que mostrará pydoc:

module1.py

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

module2.py

__all__ = ['a', 'b']

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

$ pydoc module1

Ayuda sobre el módulo module1:

NOMBRE
    módulo 1

ARCHIVO
    module1.py

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

$ pydoc module2

Ayuda sobre el módulo module2:

NOMBRE
    módulo2

ARCHIVO
    module2.py

DATOS 
    __todos__ = ['a', 'b']
     a = 'A'
     b = 'B'

Declaro __all__en todos mis módulos, así como subrayar los detalles internos, estos realmente ayudan cuando se usan cosas que nunca antes has usado en sesiones de intérpretes en vivo.


55

__all__personaliza *enfrom <module> import *

__all__personaliza *enfrom <package> import *


Un módulo es un .pyarchivo destinado a ser importado.

Un paquete es un directorio con un __init__.pyarchivo. Un paquete generalmente contiene módulos.


MÓDULOS

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

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99

__all__les permite a los humanos conocer las características "públicas" de un módulo . [ @AaronHall ] Además, pydoc los reconoce. [ @Longpoke ]

desde la importación del módulo *

Vea cómo swissy cheddarse introducen en el 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.


Las importaciones sin *no se ven afectadas por__all__


módulo de importación

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

de los nombres de importación de módulos

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

módulo de importación como nombre local

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

PAQUETES

En el __init__.pyarchivo de un paquete __all__ hay una lista de cadenas con los nombres de módulos públicos u otros objetos. Esas características están disponibles para las importaciones de comodines. Al igual que con los módulos, __all__personaliza la *importación de comodines desde el paquete. [ @MartinStettner ]

Aquí hay 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, un asterisco sin __all__un paquete , es complicado, porque el comportamiento obvio sería costoso: usar el sistema de archivos para buscar todos los módulos en el paquete. En cambio, en mi lectura de los documentos, solo se importan los objetos definidos en __init__.py:

Si __all__no está definido, la declaración from sound.effects import *no no importar todos los submódulos del paquetesound.effects en el espacio de nombres actual; solo se asegura de que el paquete sound.effectsse haya importado (posiblemente ejecutando cualquier código de inicialización __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 todos los submódulos del paquete que se cargaron explícitamente en declaraciones de importación anteriores.


Las importaciones de comodines ... deben evitarse ya que [confunden] a los lectores y muchas herramientas automatizadas.

[ PEP 8 , @ToolmakerSteve]


2
Me gusta mucho esta respuesta, pero me falta la información sobre lo que es el comportamiento predeterminado de from <package> import *sin __all__en __init__.pyque está no importando cualquiera de los módulos .
radzak

Gracias @Jatimir, aclaré lo mejor que pude sin realizar experimentos. Casi quería decir que este caso (asterisco sin todo para un paquete) se comporta igual que si __init__.pyfuera un módulo . Pero no estoy seguro de que sea exacto, o en particular si se excluyen los objetos con subrayado subrayado. Además, separé más claramente las secciones sobre MÓDULOS y PAQUETES. ¿Tus pensamientos?
Bob Stein

49

De (Un No Oficial) Python Reference Wiki :

Los nombres públicos definidos por un módulo se determinan verificando el espacio de nombres del módulo para una variable nombrada __all__; si está definido, debe ser una secuencia de cadenas que son nombres definidos o importados por ese módulo. __all__Todos los nombres dados 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 la API pública completa. Su objetivo es evitar la exportación accidental de elementos que no forman parte de la API (como los módulos de biblioteca que se importaron y utilizaron dentro del módulo).


El enlace de la lista está muerto. pero encontré texto textualmente en vdocuments.net/… y aquí: dokumen.tips/documents/reference-567bab8d6118a.html
JayRizzo

8

__all__se usa para documentar la API pública de un módulo de Python. Aunque es opcional, __all__debe usarse.

Aquí está el extracto relevante de la referencia del lenguaje Python :

Los nombres públicos definidos por un módulo se determinan verificando el espacio de nombres del módulo para una variable nombrada __all__; si se define, debe ser una secuencia de cadenas que son nombres definidos o importados por ese módulo. __all__Todos los nombres dados 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 la API pública completa. Su objetivo es evitar la exportación accidental de elementos que no forman parte de la API (como los módulos de biblioteca que se importaron y utilizaron dentro del módulo).

PEP 8 usa una redacción similar, aunque también deja en claro que los nombres importados no son 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 Establecer __all__una lista vacía indica que el módulo no tiene una API pública.

[...]

Los nombres importados siempre deben considerarse un detalle de implementación. Otros módulos no deben confiar en el acceso indirecto a dichos nombres importados a menos que sean una parte documentada explícitamente de la API del módulo que los contiene, como os.pathun __init__módulo de paquete que expone la funcionalidad de los submódulos.

Además, como se señaló en otras respuestas, __all__se utiliza para habilitar la importación de comodines para paquetes :

La declaración de importación utiliza la siguiente convención: si el __init__.pycódigo de un paquete define una lista nombrada __all__, se considera que es la lista de nombres de módulos que deben importarse cuando from package import *se encuentra.


8

Respuesta corta

__all__afecta las from <module> import *declaraciones.

Respuesta larga

Considere este ejemplo:

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

En foo/__init__.py:

  • (Implícito) Si no definimos __all__, from foo import *solo importaremos los nombres definidos en foo/__init__.py.

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

  • (Explícito) Si definimos __all__ = [ <name1>, ... ], from foo import *solo importaremos esos nombres.

Tenga en cuenta que, en el caso implícito, python no importará nombres que comiencen por _. Sin embargo, puede forzar la importación de dichos nombres utilizando__all__ .

Puede ver el documento de Python aquí .


5

__all__afecta cómo from foo import *funciona

El código que está dentro del cuerpo de un módulo (pero no en el cuerpo de una función o clase) puede usar un asterisco ( *) en una fromdeclaración:

from foo import *

Las *solicitudes de que todos los atributos del módulo foo(excepto los que comienzan con guiones bajos) se unan como variables globales en el módulo de importación. Cuando footiene un atributo __all__, el valor del atributo es la lista de los nombres que están vinculados por este tipo defrom declaración.

Si fooes un paquete y __init__.pydefine una lista llamada __all__, se considera que es la lista de nombres de submódulos que deben importarse cuando from foo import *se encuentran. Si __all__no está definido, la instrucción from foo import *importa los nombres definidos en el paquete. Esto incluye cualquier nombre definido (y submódulos cargados explícitamente) por __init__.py.

Tenga en cuenta que __all__no tiene que ser una lista. Según la documentación de la importdeclaración , si está definida, __all__debe ser una secuencia de cadenas que son nombres definidos o importados por el módulo. Por lo tanto, también puede usar una tupla para ahorrar memoria y ciclos de CPU. Simplemente no olvide una coma en caso de que el módulo defina un solo nombre público:

__all__ = ('some_name',)

Consulte también ¿Por qué es malo "importar *"?


1

Esto se define en PEP8 aquí :

Nombres de variables globales

(Esperemos que estas variables estén diseñadas para usarse solo dentro de un módulo). Las convenciones son casi las mismas que para las funciones.

Los módulos diseñados para su uso a través de from M import *deben usar el __all__mecanismo para evitar la exportación de globales, o utilizar la convención más antigua de prefijar tales globales con un guión bajo (lo que es posible que desee hacer para indicar que estos globales son "módulo no público").

PEP8 proporciona convenciones de codificación para el código de Python que comprende la biblioteca estándar en la distribución principal de Python. Cuanto más sigas esto, más cerca estarás de la intención original.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.