else - python class
¿Alguien puede explicar__all__ en Python? (9)
Explica __all__ 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á efectos programáticos:
import *
__all__
en un módulo, por ejemplo, module.py
:
__all__ = [''foo'', ''Bar'']
significa que cuando import *
del módulo, solo se importan los nombres en __all__
:
from module import * # imports foo and Bar
Herramientas de documentacion
Las herramientas de autocompletado de documentación y código también pueden (de hecho, deberían) inspeccionar __all__
para determinar qué nombres se muestran como disponibles desde un módulo.
__init__.py
hace que un directorio sea un paquete de Python
De la docs :
Los archivos
__init__.py
son necesarios para que Python trate los directorios como paquetes que contienen; esto se hace para evitar que los directorios con un nombre común, como una cadena, oculten involuntariamente los 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
solo puede ser un archivo vacío, pero también puede ejecutar el código de inicialización del paquete o establecer la variable__all__
.
Así que __init__.py
puede declarar __all__
para un paquete .
Gestionando una API:
Un paquete generalmente está formado por módulos que pueden importarse entre sí, pero que necesariamente están vinculados con un archivo __init__.py
. Ese archivo es lo que hace que el directorio sea un paquete real de Python. 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:
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 importan su paquete, así:
import package
package.foo()
package.Bar()
Y no tendrán todos los otros nombres que usó al crear sus módulos que abarrotan el espacio de nombres del package
.
__all__
en __init__.py
Después de más trabajo, tal vez haya decidido que los módulos son demasiado grandes y deben dividirse. Así que haces lo siguiente:
package/
|-__init__.py
|-module_1/
| |-__init__.py
| |-foo_implementation.py
|-module_2/
|-__init__.py
|-Bar_implementation.py
Y en cada __init__.py
declara un __all__
, por ejemplo, en el módulo_1:
from foo_implementation import *
__all__ = [''foo'']
Y el módulo_2 de __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á listo para publicar Baz
en la API de nivel superior, en su nivel superior __init__.py
podrí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 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.
Más tarde puede 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'']
Prefijando _
contra __all__
:
De forma predeterminada, Python exportará todos los nombres que no comiencen con un _
. Ciertamente podrías confiar en este mecanismo. De hecho, algunos paquetes en la biblioteca estándar de Python dependen de 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 nuevamente. Pero agrega la redundancia para las importaciones (si tiene muchas) y es fácil olvidarse de hacer esto de manera consistente, y lo último que quiere es tener que soportar indefinidamente algo que pretendía ser solo un detalle de implementación, solo porque olvidaste el prefijo de una _
al nombrar una función.
Personalmente escribo un __all__
principio 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 usar.
La mayoría de los paquetes en la biblioteca estándar también usan __all__
.
Cuando evitar __all__
tiene sentido
Tiene sentido adherirse 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 pruebas de unidad que cubren la API, y todavía está agregando activamente a la API y modificando el desarrollo.
Un decorador de export
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.
Tuve la idea para un decorador de exportación de este tipo gracias a la charla sobre el embalaje de David Beazley. Esta implementación parece funcionar bien en el importador tradicional de CPython. Si tiene un sistema o un gancho de importación especial, no lo garantizo, pero si lo adopta, es bastante trivial retroceder, solo tendrá que volver a agregar los nombres manualmente al __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 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
He estado usando Python más y más, y sigo viendo la variable __all__
establecida en diferentes archivos __init__.py
. ¿Alguien puede explicar qué hace esto?
Respuesta corta
__all__
afecta a from <module> import *
declaraciones from <module> import *
Respuesta larga
Considera 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 enfoo/__init__.py
.(Explícito) Si definimos
__all__ = []
, entoncesfrom foo import *
se importará nada.(Explícito) Si definimos
__all__ = [ <name1>, ... ]
, entoncesfrom foo import *
solo importaremos esos nombres.
Tenga en cuenta que, en el caso implícito, python no importará nombres que empiecen por _
. Sin embargo, puede forzar la importación de dichos nombres utilizando __all__
.
Puedes ver el documento de Python aquí .
__all__
personaliza el asterisco from <module> import *
__all__
personaliza el asterisco from <package> import *
Un module es un archivo .py
destinado a ser importado.
Un module es un directorio con un archivo __init__.py
. Un paquete generalmente contiene 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 ]
desde el módulo de importación *
Vea cómo el swiss
y el cheddar
son traídos al espacio de nombres local, pero no a 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) hubiera 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)
desde el módulo de importación de nombres
>>> 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)
En el archivo __init__.py
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 el *
al importar comodines desde el paquete. [ @MartinStettner ]
Aquí hay un extracto del conector MySQL de Python __init__.py
:
__all__ = [
''MySQLConnection'', ''Connect'', ''custom_error_exception'',
# Some useful constants
''FieldType'', ''FieldFlag'', ''ClientFlag'', ''CharacterSet'', ''RefreshOption'',
''HAVE_CEXT'',
# Error handling
''Error'', ''Warning'',
...etc...
]
Las importaciones con comodines ... deben evitarse, ya que [confunden] a los lectores y muchas herramientas automatizadas.
[ PEP 8 , @ToolmakerSteve]
De (un no oficial) Wiki de referencia de Python :
Los nombres públicos definidos por un módulo se determinan revisando el espacio de nombres del módulo para una variable llamada
__all__
; si se define, debe ser una secuencia de cadenas que son nombres definidos o importados por ese módulo. Los nombres dados en__all__
se consideran todos 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. Está pensado para 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).
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.
Solo estoy agregando esto para ser precisos:
Todas las demás respuestas se refieren a módulos . La pregunta original mencionó explícitamente __all__
en los archivos __init__.py
, por lo que se trata de paquetes de python.
En general, __all__
solo entra en juego cuando se utiliza la variante from xxx import *
de la declaración de import
. 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 here en detalle.
En resumen, __all__
en el 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 ). Así que __all__
especifica todos los módulos que se __all__
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 __all__
de un paquete, la declaración from package import *
no importará nada en absoluto (con las excepciones explicadas en la documentación, vea 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.
También cambia lo que pydoc mostrará:
módulo1.py
a = "A"
b = "B"
c = "C"
módulo2.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, además de subrayar los detalles internos, estos realmente ayudan cuando utilizo cosas que nunca ha usado antes en sesiones de intérpretes en vivo.
Vinculado a, pero no mencionado explícitamente aquí, es exactamente cuando se usa __all__
. Es una lista de cadenas que definen qué símbolos de un módulo se exportarán cuando se utilice from <module> import *
en el módulo.
Por ejemplo, el siguiente código en un foo.py
exporta explícitamente la bar
símbolos y baz
:
__all__ = [''bar'', ''baz'']
waz = 5
bar = 10
def baz(): return ''baz''
Estos símbolos pueden ser importados 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__
anterior está comentado, este código se ejecutará hasta su finalización, ya que el comportamiento predeterminado de import *
es importar todos los símbolos que no comiencen con un guión bajo, desde el espacio de nombres dado.
Referencia: https://docs.python.org/3.5/tutorial/modules.html#importing-from-a-package
NOTA: __all__
afecta el comportamiento from <module> import *
solamente. Los miembros que no se mencionan en __all__
todavía son accesibles desde fuera del módulo y se pueden importar con from <module> import <member>
.
__all__
se utiliza para documentar la API pública de un módulo de Python. Aunque es opcional, debe usarse __all__
.
Aquí está el extracto relevante de la referencia del lenguaje Python :
Los nombres públicos definidos por un módulo se determinan revisando el espacio de nombres del módulo para una variable llamada
__all__
; si se define, debe ser una secuencia de cadenas que son nombres definidos o importados por ese módulo. Los nombres dados en__all__
se consideran todos 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. Está pensado para 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 claro que los nombres importados no forman parte de la API pública cuando __all__
está ausente:
Para soportar mejor la introspección, los módulos deben declarar explícitamente los nombres en su API pública usando el atributo
__all__
. La configuración de__all__
en 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 depender del acceso indirecto a dichos nombres importados a menos que sean una parte documentada explícitamente de la API del módulo que lo contiene, como
os.path
o el módulo__init__
un 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 usa la siguiente convención: si el código
__init__.py
un paquete define una lista llamada__all__
, se considera la lista de nombres de módulos que deben importarse cuando se encuentrafrom package import *
.