Ejecución del código Python con opción -m o no

El intérprete de Python tiene la opción de módulo -m que “Ejecuta el módulo del módulo de biblioteca como un script”.

Con este código python a.py:

 if __name__ == "__main__": print __package__ print __name__ 

python -ma para obtener

 "" <-- Empty String __main__ 

mientras que python a.py regresa

 None <-- None __main__ 

Para mí, esas dos invocaciones parecen ser las mismas, excepto que __package__ no es None cuando se invoca con la opción -m.

Curiosamente, con python -m runpy a , obtengo lo mismo que python -ma con el módulo de python comstackdo para obtener a.pyc.

¿Cuál es la diferencia (práctica) entre estas invocaciones? Cualquier pros y contras entre ellos?

Además, Python Essential Reference de David Beazley lo explica como “La opción -m ejecuta un módulo de biblioteca como un script que se ejecuta dentro del módulo __main__ antes de la ejecución del script principal”. Qué significa eso?

Cuando use el indicador de línea de comando -m , Python importará un módulo o paquete por usted, luego lo ejecutará como un script. Cuando no usa la -m , el archivo que nombró se ejecuta solo como un script .

La distinción es importante cuando intentas ejecutar un paquete. Hay una gran diferencia entre:

 python foo/bar/baz.py 

y

 python -m foo.bar.baz 

como en este último caso, foo.bar se importa y las importaciones relativas funcionarán correctamente con foo.bar como punto de partida.

Manifestación:

 $ mkdir -p test/foo/bar $ touch test/foo/__init__.py $ touch test/foo/bar/__init__.py $ cat << EOF > test/foo/bar/baz.py > if __name__ == "__main__": > print __package__ > print __name__ > > EOF $ PYTHONPATH=test python test/foo/bar/baz.py None __main__ $ PYTHONPATH=test bin/python -m foo.bar.baz foo.bar __main__ 

Como resultado, Python tiene que preocuparse realmente por los paquetes al usar el -m . Una secuencia de comandos normal nunca puede ser un paquete, por lo que __package__ se establece en None .

Pero ejecute un paquete o módulo dentro de un paquete con -m y ahora existe al menos la posibilidad de un paquete, por lo que la variable __package__ se establece en un valor de cadena; en la demostración anterior, se establece en foo.bar , para módulos simples que no están dentro de un paquete, se establece en una cadena vacía.

En cuanto al módulo __main__ ; Python importa los scripts que se ejecutan como lo haría con un módulo regular. Se crea un nuevo objeto de módulo para contener el espacio de nombres global, almacenado en sys.modules['__main__'] . Esto es a lo que se refiere la variable __name__ , es una clave en esa estructura.

Para los paquetes, puede crear un módulo __main__.py y hacer que se ejecute al ejecutar python -m package_name ; de hecho, esa es la única manera en que puede ejecutar un paquete como un script:

 $ PYTHONPATH=test python -m foo.bar python: No module named foo.bar.__main__; 'foo.bar' is a package and cannot be directly executed $ cp test/foo/bar/baz.py test/foo/bar/__main__.py $ PYTHONPATH=test python -m foo.bar foo.bar __main__ 

Entonces, al nombrar un paquete para ejecutarse con -m , Python busca un módulo __main__ contenido en ese paquete y lo ejecuta como un script. Su nombre aún se establece en __main__ , y el objeto del módulo todavía se almacena en sys.modules['__main__'] .

Ejecución del código Python con opción -m o no

Usa la bandera -m .

Los resultados son prácticamente los mismos cuando tiene una secuencia de comandos, pero cuando desarrolla un paquete, sin la -m , no hay forma de hacer que las importaciones funcionen correctamente si desea ejecutar un subpaquete o módulo en el paquete como el Punto de entrada principal a tu progtwig (y créeme, lo he intentado).

Los docs

Como dicen los docs :

Busque en sys.path el módulo nombrado y ejecute su contenido como el módulo __main__ .

y

Al igual que con la opción -c, el directorio actual se agregará al inicio de sys.path.

asi que

 python -m pdb 

es aproximadamente equivalente a

 python /usr/lib/python3.5/pdb.py 

(asumiendo que no tiene un paquete o script en su directorio actual llamado pdb.py)

Explicación:

El comportamiento se hace “deliberadamente similar a” los scripts.

Muchos módulos de biblioteca estándar contienen código que se invoca en su ejecución como un script. Un ejemplo es el módulo timeit:

Algún código de Python está pensado para ejecutarse como un módulo: (Creo que este ejemplo es mejor que el ejemplo de la línea de comandos doc).

 $ python -m timeit '"-".join(str(n) for n in range(100))' 10000 loops, best of 3: 40.3 usec per loop $ python -m timeit '"-".join([str(n) for n in range(100)])' 10000 loops, best of 3: 33.4 usec per loop $ python -m timeit '"-".join(map(str, range(100)))' 10000 loops, best of 3: 25.2 usec per loop 

Y a partir de la nota de la versión más destacada de Python 2.4 :

La opción de línea de comando -m – python -m nombre_módulo buscará un módulo en la biblioteca estándar y lo invocará. Por ejemplo, python -m pdb es equivalente a python /usr/lib/python2.4/pdb.py

Siguiente pregunta

Además, Python Essential Reference de David Beazley lo explica como “La opción -m ejecuta un módulo de biblioteca como un script que se ejecuta dentro del módulo __main__ antes de la ejecución del script principal”.

Significa que cualquier módulo que pueda buscar con una statement de importación se puede ejecutar como el punto de entrada del progtwig; si tiene un bloque de código, generalmente cerca del final, con if __name__ == '__main__':

-m sin agregar el directorio actual a la ruta:

Un comentario aquí en otra parte dice:

La opción -m también agrega el directorio actual a sys.path, obviamente es un problema de seguridad (ver: ataque de precarga). Este comportamiento es similar al orden de búsqueda de bibliotecas en Windows (antes de que se hubiera endurecido recientemente). Es una pena que Python no siga la tendencia y no ofrezca una forma sencilla de deshabilitar la adición. a sys.path

Bueno, esto demuestra el posible problema – (en Windows elimine las comillas):

 echo "import sys; print(sys.version)" > pdb.py python -m pdb 3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] 

Use el indicador -I para bloquear esto en entornos de producción (nuevos en la versión 3.4):

 python -Im pdb usage: pdb.py [-c command] ... pyfile [arg] ... etc... 

de los documentos :

-I

Ejecutar Python en modo aislado. Esto también implica -E y -s. En modo aislado, sys.path no contiene el directorio del script ni el directorio site-packages del usuario. Todas las variables de entorno de PYTHON * también se ignoran. Se pueden imponer restricciones adicionales para evitar que el usuario inyecte código malicioso.

¿Qué hace __package__ ?

Sin embargo, permite las importaciones relativas explícitas, no especialmente relacionadas con esta pregunta; vea esta respuesta aquí: ¿Cuál es el propósito del atributo “__package__” en Python?

La razón principal para ejecutar un módulo (o paquete) como un script con -m es simplificar la implementación, especialmente en Windows. Puede instalar scripts en el mismo lugar en la biblioteca de Python donde normalmente van los módulos, en lugar de contaminar directorios PATH o ejecutables globales como ~ / .local (el directorio de scripts por usuario es ridículamente difícil de encontrar en Windows).

Luego simplemente escribe -m y Python encuentra el script automáticamente. Por ejemplo, python -m pip encontrará el pip correcto para la misma instancia del intérprete de Python que lo ejecuta. Sin -m, si el usuario tiene varias versiones de Python instaladas, ¿cuál sería el pip “global”?

Si el usuario prefiere los puntos de entrada “clásicos” para los guiones de línea de comandos, estos pueden agregarse fácilmente como guiones pequeños en algún lugar de PATH, o pip puede crearlos en el momento de la instalación con el parámetro entry_points en setup.py.

Así que solo __name__ == '__main__' e ignore otros detalles de implementación no confiables.