À quoi sert le commutateur -m?

174

Pourriez-vous m'expliquer quelle est la différence entre appeler

python -m mymod1 mymod2.py args

et

python mymod1.py mymod2.py args

Il semble dans les deux cas mymod1.pys'appelle et sys.argvest

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

Alors à quoi sert le -mcommutateur?

Charles Brunet
la source
Veuillez me corriger si je me trompe, mais -msemble rechercher mymod1dans le chemin de la bibliothèque par défaut. Exemple: python -m SimpleHTTPServerfonctionne, mais python SimpleHTTPServeréchoue avec can't open file 'SimpleHTTPServer': [Errno 2] No such file or directory.
Basj
7
En fait, j'ai trouvé la réponse ici plus claire: stackoverflow.com/questions/46319694/…
Casebash

Réponses:

137

La première ligne de la Rationalesection de la PEP 338 dit:

Python 2.4 ajoute le commutateur de ligne de commande -m pour permettre aux modules d'être localisés à l'aide de l'espace de noms du module Python pour une exécution en tant que scripts. Les exemples motivants étaient des modules de bibliothèque standard tels que pdb et profile, et l'implémentation de Python 2.4 convient parfaitement à cet objectif limité.

Ainsi, vous pouvez spécifier n'importe quel module dans le chemin de recherche de Python de cette façon, pas seulement les fichiers dans le répertoire actuel. Vous avez raison, cela python mymod1.py mymod2.py argsa exactement le même effet. La première ligne de la Scope of this proposalsection indique:

Dans Python 2.4, un module localisé à l'aide de -m est exécuté comme si son nom de fichier avait été fourni sur la ligne de commande.

Avec -mplus, c'est possible, comme travailler avec des modules qui font partie d'un package, etc. C'est ce dont parle le reste de PEP 338. Lisez-le pour plus d'informations.

agf
la source
47
Mon utilisation préférée de -mest python -m SimpleHTTPServer. Vraiment pratique lorsque j'ai besoin de partager des fichiers sans utiliser de clé USB.
arifwn
21
@arifwn Exécuter Python3 nécessite une légère mise à jour car python -m http.serveret c'est toujours génial!
Kit Roed
12
TL; DR: 1) Vous pouvez exécuter python -m package.subpackage.moduleet la machine de résolution normale sera utilisée, vous n'avez pas à indiquer le .pyfichier exact . 2) Il est possible d'effectuer des importations relatives à partir du module exécuté, sans aucune solution de contournement, car son package sera chargé en cours de route. 3) Les importations absolues seront basées sur votre répertoire actuel, et non sur le répertoire où se trouve le .pyfichier ( ''est à la tête de sys.path, plutôt que /path/to/mysi le script est à /path/to/my/script.py).
clacke le
Ce que cette réponse ne précise pas, c'est que cela ne fonctionne que sur le sous-ensemble de modules qui sont exécutables, c'est-à-dire qui ont un __main__.pyfichier. La plupart ne le font pas et vont casser par exemple python -m sys 'print(sys.version)'avec python: No code object available for sys. Je vous suggère de préciser cela dans la réponse.
smci
19

Cela vaut la peine de mentionner que cela ne fonctionne que si le package contient un fichier.__main__.py Sinon, ce package ne peut pas être exécuté directement.

python -m some_package some_arguments

L'interpréteur python recherchera un __main__.pyfichier dans le chemin du package à exécuter. C'est équivalent à:

python path_to_package/__main__.py somearguments

Il exécutera le contenu après:

if __name__ == "__main__":
Marquez.Z
la source
2
Qu'en est-il du fichier d'initialisation du package? En présence du fichier principal, init sera-t-il également appelé?
variable
@variable Oui init .py sera invoqué avant l'appel principal .py
Mark Rucker
1

Bien que cette question ait été posée et répondu à plusieurs reprises (par exemple, ici , ici , ici et ici ), à mon avis, aucune réponse existante ne rend compte de manière complète ou concise toutes les implications du -mdrapeau. Par conséquent, ce qui suit tentera d'améliorer ce qui a précédé.

Introduction (TLDR)

La -mcommande fait beaucoup de choses dont toutes ne seront pas nécessairement nécessaires tout le temps. En bref, il: (1) permet aux scripts python d'être exécutés via le nom de module plutôt que le nom de fichier (2) permet de choisir un répertoire à ajouter sys.pathpour la importrésolution et (3) permet aux scripts python avec des importations relatives d'être exécutés à partir de la ligne de commande .

Préliminaires

Pour expliquer le -mdrapeau, nous devons d'abord clarifier un peu la terminologie.

Premièrement, l'unité organisationnelle principale de Python est connue sous le nom de module . Les modules sont disponibles en deux versions: les modules de code et les modules de package. Un module de code est un fichier contenant du code exécutable python. Un module de package est un répertoire qui contient d'autres modules (soit des modules de code, soit des modules de package). Les types de modules de code *.pyles plus courants sont les fichiers tandis que les types les plus courants de modules de packages sont les répertoires contenant un __init__.pyfichier.

Deuxièmement, tous les modules peuvent être identifiés de manière unique de deux manières distinctes: <modulename>et <filename>. Les modules sont le plus souvent identifiés par nom de module dans le code Python (par exemple, import <modulename>) et par nom de fichier sur la ligne de commande (par exemple, python <filename>). Tous les interpréteurs Python peuvent convertir les noms de modules en noms de fichiers via un ensemble de règles bien définies. Ces règles dépendent de la sys.pathvariable et par conséquent le mappage peut être modifié en changeant cette valeur (pour plus d'informations sur la façon dont cela est fait, voir PEP 302 ).

Troisièmement, tous les modules (à la fois le code et le package) peuvent être exécutés (nous entendons par là que le code associé au module sera évalué par l'interpréteur Python). En fonction de la méthode d'exécution et du type de module, le code évalué et le moment peuvent changer un peu. Par exemple, si on exécute un module de package via python <filename>alors <filename>/__init__.pysera évalué suivi de <filename>/__main__.py. D'un autre côté, si l'on exécute ce même module de package via import <modulename>alors seuls les packages __init__.pyseront exécutés.

Développement historique de -m

L'indicateur -m a été introduit pour la première fois dans Python 2.4.1 . Initialement, son seul objectif était de fournir un moyen alternatif d'identifier un module python à exécuter. Autrement dit, si nous connaissions à la fois le <filename>et <modulename>pour un module, les deux commandes suivantes étaient équivalentes: python <filename> <args>et python -m <modulename> <args>. De plus, selon PEP 338, cette itération -mne fonctionnait qu'avec des noms de modules de premier niveau (c'est-à-dire des modules qui pouvaient être trouvés directement sur sys.path sans aucun paquetage intermédiaire).

Avec l'achèvement du PEP 338, la -mfonctionnalité a été étendue pour prendre en charge les <modulename>représentations au-delà des noms de module de niveau supérieur. Cela signifiait des noms tels que http.serverdésormais entièrement pris en charge. Cette amélioration signifiait également que tous les packages d'un module étaient désormais chargés (c'est-à-dire que tous les __init__.pyfichiers de packages étaient évalués), avec le module lui-même.

La dernière amélioration majeure des fonctionnalités -mest venue avec PEP 366 . Avec cette mise -mà jour, nous avons acquis la capacité de prendre en charge non seulement les importations absolues, mais également les importations relatives explicites. Ceci a été réalisé en modifiant la __package__variable du module nommé dans la -mcommande.

Cas d'utilisation

Il existe deux cas d'utilisation notables pour l'indicateur -m:

  1. Exécuter des modules à partir de la ligne de commande dont on peut ne pas connaître leur nom de fichier. Ce cas d'utilisation tire parti du fait que l'interpréteur Python sait comment convertir les noms de modules en noms de fichiers. Ceci est particulièrement avantageux lorsque l'on veut exécuter des modules stdlib ou des modules tiers à partir de la ligne de commande. Par exemple, très peu de gens connaissent le nom de fichier du http.servermodule mais la plupart des gens connaissent son nom de module afin que nous puissions l'exécuter à partir de la ligne de commande en utilisant python -m http.server.

  2. Pour exécuter un package local contenant des importations absolues sans avoir besoin de l'installer. Ce cas d'utilisation est détaillé dans PEP 338 et tire parti du fait que le répertoire de travail actuel est ajouté sys.pathplutôt que le répertoire du module. Ce cas d'utilisation est très similaire à l'utilisation pip install -e .pour installer un package en mode développement / édition.

Les lacunes

Avec toutes les améliorations apportées au -mfil des ans, il présente encore un inconvénient majeur: il ne peut exécuter que des modules de code écrits en python (c'est-à-dire * .py). Par exemple, si -mest utilisé pour exécuter un module de code compilé en C, l'erreur suivante sera produite No code object available for <modulename>(voir ici pour plus de détails).

Comparaisons détaillées

Effets de l'exécution du module via la commande python (ie, python <filename>):

  • sys.path est modifié pour inclure le répertoire final dans <filename>
  • __name__ est réglé sur '__main__'
  • __package__ est réglé sur None
  • __init__.py n'est évalué pour aucun package (y compris le sien pour les modules de package)
  • __main__.pyest évalué pour les modules de package; le code est évalué pour les modules de code.

Effets de l'exécution du module via une instruction d'importation (c'est-à-dire import <modulename>):

  • sys.pathest pas modifié de quelque façon
  • __name__ prend la forme absolue de <modulename>
  • __package__ est défini sur le package parent immédiat dans <modulename>
  • __init__.py est évalué pour tous les packages (y compris le sien pour les modules de package)
  • __main__.pyn'est pas évalué pour les modules de package; le code est évalué pour les modules de code

Effets de l'exécution du module via l'option -m (c'est-à-dire python -m <modulename>):

  • sys.path est modifié pour inclure le répertoire courant
  • __name__ est réglé sur '__main__'
  • __package__ est défini sur le package parent immédiat dans <modulename>
  • __init__.py est évalué pour tous les packages (y compris le sien pour les modules de package)
  • __main__.pyest évalué pour les modules de package; le code est évalué pour les modules de code

Conclusion

L' -mindicateur est, dans sa forme la plus simple, un moyen d'exécuter des scripts python à partir de la ligne de commande en utilisant des noms de modules plutôt que des noms de fichiers. De plus, -mfournit des fonctionnalités supplémentaires qui combinent la puissance des importinstructions (par exemple, la prise en charge d'importations relatives explicites et l' __init__évaluation automatique des packages ) avec la commodité de la ligne de commande python.

Mark Rucker
la source
Pourriez-vous également ajouter l'utilisation de l'invocation de package en utilisant python -m packagenamecomme mentionné ici: stackoverflow.com/a/53772635/1779091
variable
@variable bonne idée, j'ai ajouté une section "Cas d'utilisation" qui inclut cela.
Mark Rucker le