¿Cuál es el propósito del modificador -m?

174

¿Podría explicarme cuál es la diferencia entre llamar

python -m mymod1 mymod2.py args

y

python mymod1.py mymod2.py args

Parece que en ambos casos mymod1.pyse llama y sys.argves

['mymod1.py', 'mymod2.py', 'args']

Entonces, ¿para qué sirve el -mcambio?

Charles Brunet
fuente
Corríjame si me equivoco, pero -mparece buscar mymod1en la ruta de la biblioteca predeterminada. Ejemplo: python -m SimpleHTTPServerfunciona, mientras que python SimpleHTTPServerfalla con can't open file 'SimpleHTTPServer': [Errno 2] No such file or directory.
Basj
77
De hecho, encontré la respuesta aquí más clara: stackoverflow.com/questions/46319694/…
Casebash

Respuestas:

137

La primera línea de la Rationalesección de PEP 338 dice:

Python 2.4 agrega la línea de comando switch -m para permitir que los módulos se ubiquen utilizando el espacio de nombres del módulo Python para su ejecución como scripts. Los ejemplos motivadores fueron módulos de biblioteca estándar como pdb y profile, y la implementación de Python 2.4 está bien para este propósito limitado.

Por lo tanto, puede especificar cualquier módulo en la ruta de búsqueda de Python de esta manera, no solo los archivos en el directorio actual. Tienes razón que python mymod1.py mymod2.py argstiene exactamente el mismo efecto. La primera línea de la Scope of this proposalsección dice:

En Python 2.4, un módulo ubicado usando -m se ejecuta como si su nombre de archivo hubiera sido proporcionado en la línea de comando.

Con -mmás es posible, como trabajar con módulos que forman parte de un paquete, etc. De eso se trata el resto de PEP 338. Léelo para más información.

agf
fuente
47
Mi uso favorito de -mes python -m SimpleHTTPServer. Realmente útil cuando necesito compartir algunos archivos sin usar una unidad flash USB.
arifwn
21
@arifwn ¡Ejecutar Python3 requiere una ligera actualización python -m http.servery esto sigue siendo increíble!
Kit Roed
12
TL; DR: 1) Puede ejecutar python -m package.subpackage.moduley se utilizará la maquinaria de resolución normal, no tiene que señalar el .pyarchivo exacto . 2) Es posible realizar importaciones relativas desde el módulo que se ejecuta, sin ninguna solución, ya que su paquete se cargará en el camino. 3) Las importaciones absolutas se basarán en su directorio actual, no en el directorio donde está el .pyarchivo ( ''está al frente de sys.path, en lugar de /path/to/my, si el script está en /path/to/my/script.py).
clacke
Lo que esta respuesta no deja en claro es que esto solo funciona en el subconjunto de módulos que son ejecutables, es decir, tienen un __main__.pyarchivo. La mayoría no lo hace y se romperá, por ejemplo, python -m sys 'print(sys.version)'falla python: No code object available for sys. Te sugiero que dejes eso claro en la respuesta.
smci
19

Vale la pena mencionar que esto solo funciona si el paquete tiene un archivo.__main__.py De lo contrario, este paquete no se puede ejecutar directamente.

python -m some_package some_arguments

El intérprete de Python buscará un __main__.pyarchivo en la ruta del paquete para ejecutar. Es equivalente a:

python path_to_package/__main__.py somearguments

Ejecutará el contenido después de:

if __name__ == "__main__":
Marquez.Z
fuente
2
¿Qué pasa con el archivo de inicio del paquete? En presencia del archivo principal, ¿se invocará init también?
variable
@variable Sí se invocará init .py antes de invocar .py principal
Mark Rucker
1

A pesar de que esta pregunta ha sido formulada y respondida varias veces (por ejemplo, aquí , aquí , aquí y aquí ), en mi opinión, ninguna respuesta existente captura de manera completa o concisa todas las implicaciones de la -mbandera. Por lo tanto, lo siguiente intentará mejorar lo que vino antes.

Introducción (TLDR)

El -mcomando hace muchas cosas, no todas necesariamente serán necesarias todo el tiempo. En resumen: (1) permite scripts de Python que se ejecutan a través de modulename en lugar de nombre de archivo (2) permite elegir un directorio para agregar a sys.pathde importresolución y (3) permite a los scripts de Python con las importaciones en relación a ejecutar desde la línea de comandos .

Preliminares

Para explicar la -mbandera, primero debemos aclarar un poco la terminología.

Primero, la unidad organizativa primaria de Python se conoce como módulo . Los módulos vienen en uno de dos sabores: módulos de código y módulos de paquete. Un módulo de código es cualquier archivo que contiene código ejecutable de Python. Un módulo de paquete es un directorio que contiene otros módulos (módulos de código o módulos de paquete). El tipo más común de módulos de código son *.pyarchivos, mientras que el tipo más común de módulos de paquete son directorios que contienen un __init__.pyarchivo.

En segundo lugar, todos los módulos pueden identificarse de manera única de dos maneras distintas: <modulename>y <filename>. Los módulos se identifican con mayor frecuencia por el nombre del módulo en el código Python (por ejemplo, import <modulename>) y por el nombre del archivo en la línea de comando (por ejemplo, python <filename>). Todos los intérpretes de Python pueden convertir nombres de módulos en nombres de archivos mediante un conjunto de reglas bien definidas. Estas reglas dependen de la sys.pathvariable y, por lo tanto, el mapeo se puede modificar cambiando este valor (para más información sobre cómo se hace, consulte PEP 302 ).

En tercer lugar, todos los módulos (tanto el código como el paquete) se pueden ejecutar (con lo que queremos decir que el intérprete de Python evaluará el código asociado con el módulo). Dependiendo del método de ejecución y del tipo de módulo, qué código se evalúa y cuándo puede cambiar bastante. Por ejemplo, si uno ejecuta un módulo de paquete a través de python <filename>entonces <filename>/__init__.pyserá evaluado seguido por <filename>/__main__.py. Por otro lado, si uno ejecuta ese mismo módulo de paquete a través de import <modulename>entonces, solo __init__.pyse ejecutarán los paquetes .

Desarrollo histórico de -m

La bandera -m se introdujo por primera vez en Python 2.4.1 . Inicialmente, su único propósito era proporcionar un medio alternativo para identificar un módulo de Python para ejecutar. Es decir, si supiéramos tanto el <filename>y <modulename>para un módulo, los siguientes dos comandos son equivalentes: python <filename> <args>y python -m <modulename> <args>. Además, de acuerdo con PEP 338, esta iteración -msolo funcionaba con nombres de módulos de nivel superior (es decir, módulos que se podían encontrar directamente en sys.path sin ningún paquete intermedio).

Con la finalización de PEP 338, la -mfuncionalidad se amplió para admitir <modulename>representaciones más allá de los nombres de módulos de nivel superior. Esto significaba nombres como http.serverahora totalmente compatibles. Esta mejora también significó que todos los paquetes en un módulo ahora se cargaron (es decir, __init__.pyse evaluaron todos los archivos de paquete ), junto con el módulo en sí.

La mejora de características principales final -mvino con PEP 366 . Con esta actualización -mganó la capacidad de admitir no solo importaciones absolutas sino también importaciones relativas explícitas. Esto se logró modificando la __package__variable para el módulo nombrado en el -mcomando.

Casos de uso

Hay dos casos de uso notables para el indicador -m:

  1. Para ejecutar módulos desde la línea de comandos para los cuales uno puede no conocer su nombre de archivo. Este caso de uso aprovecha el hecho de que el intérprete de Python sabe cómo convertir nombres de módulos a nombres de archivos. Esto es particularmente ventajoso cuando se quiere ejecutar módulos stdlib o módulos de terceros desde la línea de comandos. Por ejemplo, muy pocas personas conocen el nombre del archivo para el http.servermódulo, pero la mayoría de las personas sí conoce el nombre del módulo, por lo que podemos ejecutarlo desde la línea de comandos python -m http.server.

  2. Para ejecutar un paquete local que contenga importaciones absolutas sin necesidad de instalarlo. Este caso de uso se detalla en PEP 338 y aprovecha el hecho de que el directorio de trabajo actual se agrega en sys.pathlugar del directorio del módulo. Este caso de uso es muy similar al uso pip install -e .para instalar un paquete en modo desarrollo / edición.

Deficiencias

Con todas las mejoras realizadas a lo -mlargo de los años, todavía tiene una deficiencia importante: solo puede ejecutar módulos de código escritos en python (es decir, * .py). Por ejemplo, si -mse usa para ejecutar un módulo de código compilado en C, se generará el siguiente error No code object available for <modulename>(consulte aquí para obtener más detalles).

Comparaciones detalladas

Efectos de la ejecución del módulo a través del comando python (es decir, python <filename>):

  • sys.path se modifica para incluir el directorio final en <filename>
  • __name__ se establece en '__main__'
  • __package__ se establece en None
  • __init__.py no se evalúa para ningún paquete (incluido el suyo para módulos de paquete)
  • __main__.pyse evalúa para módulos de paquete; El código se evalúa para los módulos de código.

Efectos de la ejecución del módulo a través de la declaración de importación (es decir, import <modulename>):

  • sys.pathse no modificadas en forma alguna
  • __name__ se establece en la forma absoluta de <modulename>
  • __package__ se establece en el paquete primario inmediato en <modulename>
  • __init__.py se evalúa para todos los paquetes (incluido el suyo para los módulos de paquete)
  • __main__.pyse no evaluado para los módulos del paquete; el código se evalúa para los módulos de código

Efectos de la ejecución del módulo a través de la bandera -m (es decir, python -m <modulename>):

  • sys.path se modifica para incluir el directorio actual
  • __name__ se establece en '__main__'
  • __package__ se establece en el paquete primario inmediato en <modulename>
  • __init__.py se evalúa para todos los paquetes (incluido el suyo para los módulos de paquete)
  • __main__.pyse evalúa para módulos de paquete; el código se evalúa para los módulos de código

Conclusión

La -mbandera es, en su forma más simple, un medio para ejecutar scripts de Python desde la línea de comandos utilizando nombres de módulos en lugar de nombres de archivos. Además, -mproporciona una funcionalidad adicional que combina el poder de las importdeclaraciones (por ejemplo, soporte para importaciones relativas explícitas y __init__evaluación automática de paquetes ) con la conveniencia de la línea de comandos de Python.

Mark Rucker
fuente
¿Podría agregar también el uso del paquete de invocación usando python -m packagenamecomo se menciona aquí: stackoverflow.com/a/53772635/1779091
variable
@variable buena idea, agregué una sección de "Caso de uso" que incluye eso.
Mark Rucker