¿Alguien puede explicarlo todo en Python?


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

Author: varikin, 0000-00-00

5 answers

Es una lista de objetos públicos de ese módulo, tal como lo interpreta import *. Anula el valor predeterminado de ocultar todo lo que comienza con un guion bajo.

 334
Author: Jimmy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-08 15:50:12

Enlazado a, pero no explícitamente mencionado aquí, es exactamente cuando se usa __all__. Es una lista de cadenas que definen qué símbolos en un módulo se exportarán cuando se use from <module> import * 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'

Estos símbolos se pueden importar de la siguiente manera:

from foo import *

print bar
print baz

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

Si se comenta el __all__ anterior, este código se ejecutará hasta completarse, ya que el comportamiento predeterminado de import * es para importar todos los símbolos que no comiencen con un guion bajo, desde el espacio de nombres dado.

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

NOTA: __all__ solo afecta al comportamiento from <module> import *. Los miembros que no se mencionan en __all__ siguen siendo accesibles desde fuera del módulo y se pueden importar con from <module> import <member>.

 696
Author: Alec Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-14 09:35:53

Solo estoy agregando esto para ser preciso:

Todas las demás respuestas se refieren a módulos. La pregunta original mencionaba explícitamente __all__ en los archivos __init__.py, por lo que se trata de paquetes de python .

Generalmente, __all__ solo entra en juego cuando se usa la variante from xxx import * de la instrucción import. 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 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 especificar nombres dentro del módulo). Así que __all__ especifica todos los módulos que se cargarán e importarán en el espacio de nombres actual cuando usemos from package import *.

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

Por otro lado, si omite __all__ en un módulo, la "importación marcada" importará todos los nombres (no comenzando con un guion bajo) definidos en el módulo.

 138
Author: MartinStettner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-16 19:01:48

¿Explicar _ _ todo _ _ en Python?

Sigo viendo la variable __all__ establecida en diferentes archivos __init__.py.

, ¿Qué hace esto?

¿Qué hace __all__?

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

También tendrá programática afecta:

import *

__all__ en un módulo, por ejemplo, module.py:

__all__ = ['foo', 'Bar']

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

from module import *               # imports foo and Bar

Herramientas de documentación

La documentación y las herramientas de autocompletado de código también pueden (de hecho, deberían) inspeccionar __all__ para determinar qué nombres mostrar como disponibles en un módulo.

__init__.py convierte un directorio en un paquete Python

De los documentos :

Los archivos __init__.py son necesarios para hacer que Python trate los directorios como si contuvieran paquetes; esto se hace para evitar que los directorios con un nombre común, como string, oculten involuntariamente módulos válidos que ocurran más adelante en la ruta de búsqueda del módulo.

En el caso más simple, __init__.py puede ser solo un archivo vacío, pero también puede ejecutar código de inicialización para el paquete o establecer la variable __all__.

Así que el __init__.py puede declarar el __all__ para un paquete .

Administrar una API:

Un paquete se compone típicamente de módulos que pueden importarse entre sí, pero que están necesariamente unidos con un archivo __init__.py. Ese archivo es lo que hace que el directorio sea un paquete Python real. Por ejemplo, digamos que tienes lo siguiente:

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

En el __init__.py escribes: {[70]]}

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 tu 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 espacio de nombres package.

{[19] {} en[20]}

Después de más trabajo, tal vez haya decidido que los módulos son demasiado grandes y deben dividirse. Así que haz lo siguiente:

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

Y en cada __init__.py se declara un __all__, por ejemplo, en module_1:

from foo_implementation import *
__all__ = ['foo']

Y module_2's __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 __init__.py, por ejemplo, en module_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 tu 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 son conscientes de la disponibilidad de Baz, pueden usarlo:

import package
package.Baz()

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

Luego puedes cambiar eso cuando Baz esté listo para el prime time:

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

Prefijando _ versus __all__:

De forma predeterminada, Python exportará todos los nombres que no comiencen con _. Ciertamente podría confiar en este mecanismo. Algunos paquetes en la biblioteca estándar de Python, de hecho, hacen se basan en esto, pero para hacerlo, alias 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 de nuevo. Pero agrega la redundancia para las importaciones (si tiene muchas) y es fácil olvidar hacer esto consistentemente - y lo último que desea es tener que soportar indefinidamente algo que pretendía ser solo un detalle de implementación, solo porque olvidó anteponer un _ al nombrar una función.

I escribir personalmente un __all__ al principio de mi ciclo de desarrollo para módulos para que otros que puedan usar mi código sepan lo que deben usar y no usar.

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

Cuando evitar __all__ tiene sentido

Tiene sentido atenerse a la convención de prefijos _ en lugar de __all__ cuando:

  • Todavía estás en el modo de desarrollo inicial y no tienes usuarios, y estás ajustando constantemente tu API.
  • Tal vez usted tiene usuarios, pero tiene unittests 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 debe escribir los nombres de las funciones y clases que se exportan dos veces, y la información se mantiene separada de las definiciones. podríamos usar un decorador para resolver este problema.

Tengo la idea de un decorador de exportación de David Beazley hablar 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 retroceder - solo tendrá que agregar manualmente los nombres de nuevo en el __all__

Así que en, por ejemplo, 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__, 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 con 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
 110
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-27 13:06:31

También cambia lo que pydoc mostrará:

Module1.py

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

Module2.py

__all__ = ['a', 'b']

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

$ pydoc module1

Help on module module1:

NAME
    module1

FILE
    module1.py

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

$ pydoc module2

Help on module module2:

NAME
    module2

FILE
    module2.py

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

Declaro __all__ en todos mis módulos, así como subrayar los detalles internos, estos realmente ayudan cuando se utilizan cosas que nunca has utilizado antes en sesiones de intérprete en vivo.

 83
Author: L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-15 03:22:29