¿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?
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.
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>
.
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.
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
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.
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