Qual é o objetivo da opção -m?


174

Você poderia me explicar qual é a diferença entre chamar

python -m mymod1 mymod2.py args

e

python mymod1.py mymod2.py args

Parece que em ambos os casos mymod1.pyé chamado e sys.argvé

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

Então, para que serve essa -mtroca?


Corrija-me se estiver errado, mas -mparece procurar mymod1no caminho da biblioteca padrão. Exemplo: python -m SimpleHTTPServerfunciona, enquanto python SimpleHTTPServerfalha com can't open file 'SimpleHTTPServer': [Errno 2] No such file or directory.
Basj

7
Na verdade, eu encontrei a resposta aqui mais clara: stackoverflow.com/questions/46319694/…
Casebash

Respostas:


137

A primeira linha da Rationaleseção do PEP 338 diz:

O Python 2.4 adiciona a opção de linha de comando -m para permitir que os módulos sejam localizados usando o namespace do módulo Python para execução como scripts. Os exemplos motivadores foram módulos de biblioteca padrão, como pdb e profile, e a implementação do Python 2.4 é adequada para esse propósito limitado.

Assim, você pode especificar qualquer módulo no caminho de pesquisa do Python dessa maneira, não apenas os arquivos no diretório atual. Você está certo de que python mymod1.py mymod2.py argstem exatamente o mesmo efeito. A primeira linha da Scope of this proposalseção declara:

No Python 2.4, um módulo localizado usando -m é executado como se seu nome de arquivo tivesse sido fornecido na linha de comando.

Com -mmais é possível, como trabalhar com módulos que fazem parte de um pacote, etc. É disso que trata o restante do PEP 338. Leia para mais informações.


47
Meu uso favorito de -mé python -m SimpleHTTPServer. Realmente útil quando preciso compartilhar alguns arquivos sem usar uma unidade flash USB.
Arifwn 30/09/11

21
@arifwn A execução do Python3 requer uma pequena atualização, python -m http.servere isso ainda é incrível!
precisa

12
TL; DR: 1) Você pode executar python -m package.subpackage.modulee o mecanismo de resolução normal será usado; não é necessário apontar o .pyarquivo exato . 2) É possível fazer importações relativas do módulo que é executado, sem nenhuma solução alternativa, porque seu pacote será carregado ao longo do caminho. 3) As importações absolutas serão baseadas no diretório atual, não no diretório em que o .pyarquivo está ( ''está no topo sys.path, e não /path/to/myse o script estiver /path/to/my/script.py).
Clacke

O que esta resposta não deixa claro é que isso funciona apenas no subconjunto de módulos executáveis, ou seja, possui um __main__.pyarquivo. A maioria não quebra e python -m sys 'print(sys.version)'falha, por exemplo, falha python: No code object available for sys. Sugira que você deixe isso claro na resposta.
smci

19

Vale ressaltar que isso só funciona se o pacote tiver um arquivo.__main__.py Caso contrário, este pacote não poderá ser executado diretamente.

python -m some_package some_arguments

O intérprete python procurará um __main__.pyarquivo no caminho do pacote para executar. É equivalente a:

python path_to_package/__main__.py somearguments

Ele executará o conteúdo após:

if __name__ == "__main__":

2
E o arquivo init do pacote? Na presença do arquivo principal, o init também será chamado?
variável

@ variável Sim, o init .py será chamado antes do principal .py
Mark Rucker

1

Apesar de esta pergunta ter sido feita e respondida várias vezes (por exemplo, aqui , aqui , aqui e aqui ), na minha opinião, nenhuma resposta existente captura total ou concisamente todas as implicações da -mbandeira. Portanto, o seguinte tentará melhorar o que veio antes.

Introdução (TLDR)

O -mcomando faz muitas coisas, nem todas serão necessariamente necessárias o tempo todo. Em suma,: (1) permite scripts python para ser executado via modulename em vez de nome de arquivo (2) permite escolher um diretório para adicionar ao sys.pathde importresolução e (3) permite que os scripts python com importações em relação a ser executado a partir da linha de comando .

Preliminares

Para explicar a -mbandeira, primeiro precisamos esclarecer um pouco a terminologia.

Primeiro, a principal unidade organizacional do Python é conhecida como módulo . Os módulos vêm em um de dois tipos: módulos de código e módulos de pacote. Um módulo de código é qualquer arquivo que contém código executável python. Um módulo de pacote é um diretório que contém outros módulos (módulos de código ou módulos de pacote). O tipo mais comum de módulos de código são os *.pyarquivos, enquanto o tipo mais comum de módulos de pacote são os diretórios que contêm um __init__.pyarquivo.

Segundo, todos os módulos podem ser identificados exclusivamente de duas maneiras distintas: <modulename>e <filename>. Os módulos geralmente são identificados pelo nome do módulo no código Python (por exemplo, import <modulename>) e pelo nome do arquivo na linha de comando (por exemplo,python <filename> ). Todos os intérpretes Python podem converter nomes de módulos em nomes de arquivos por meio de um conjunto de regras bem definidas. Essas regras dependem da sys.pathvariável e, portanto, o mapeamento pode ser alterado alterando esse valor (para mais informações sobre como isso é feito, consulte PEP 302 ).

Terceiro, todos os módulos (código e pacote) podem ser executados (com o que queremos dizer que o código associado ao módulo será avaliado pelo intérprete Python). Dependendo do método de execução e do tipo de módulo, qual código é avaliado e quando pode mudar um pouco. Por exemplo, se alguém executar um módulo de pacote via, python <filename>então <filename>/__init__.pyserá avaliado seguido por <filename>/__main__.py. Por outro lado, se alguém executar o mesmo módulo de pacote via import <modulename>, somente os pacotes __init__.pyserão executados.

Desenvolvimento Histórico de -m

O sinalizador -m foi introduzido pela primeira vez no Python 2.4.1 . Inicialmente, seu único objetivo era fornecer um meio alternativo de identificar um módulo python para executar. Isto é, se soubéssemos tanto do <filename>e <modulename>para um módulo, em seguida, os dois comandos a seguir são equivalentes: python <filename> <args>e python -m <modulename> <args>. Além disso, de acordo com a PEP 338 essa iteração -mfuncionava apenas com nomes de módulos de nível superior (ou seja, módulos que podiam ser encontrados diretamente no sys.path sem nenhum pacote intermediário).

Com a conclusão do PEP 338, a -mfuncionalidade foi estendida para suportar <modulename>representações além dos nomes de módulo de nível superior. Isso significava nomes como http.serveragora eram totalmente suportados. Esse aprimoramento também significou que todos os pacotes em um módulo agora estavam carregados (ou seja, todos os pacotes__init__.py arquivos de foram avaliados), junto com o próprio módulo.

O aprimoramento final dos principais recursos -mveio com o PEP 366 . Com essa atualização, -mganhou a capacidade de suportar não apenas importações absolutas, mas também importações relativas explícitas. Isso foi obtido modificando a __package__variável do módulo nomeado no diretório-m comando.

Casos de Uso

Existem dois casos de uso notáveis ​​para o sinalizador -m:

  1. Para executar módulos a partir da linha de comando para os quais um pode não saber seu nome de arquivo. Esse caso de uso aproveita o fato de que o intérprete Python sabe como converter nomes de módulos em nomes de arquivos. Isso é particularmente vantajoso quando se deseja executar módulos stdlib ou módulo de terceiros a partir da linha de comando. Por exemplo, poucas pessoas sabem o nome do arquivo para o http.servermódulo, mas a maioria conhece o nome do módulo para que possamos executá-lo na linha de comando usando python -m http.server.

  2. Para executar um pacote local contendo importações absolutas sem a necessidade de instalá-lo. Este caso de uso é detalhado no PEP 338 e aproveita o fato de o diretório de trabalho atual ser adicionado ao sys.pathinvés do diretório do módulo. Esse caso de uso é muito semelhante ao uso pip install -e .para instalar um pacote no modo de desenvolvimento / edição.

Deficiências

Com todos os aprimoramentos feitos ao -mlongo dos anos, ele ainda possui uma grande falha - ele pode executar apenas módulos de código escritos em python (ou seja, * .py). Por exemplo, se -mfor usado para executar um módulo de código compilado em C, o seguinte erro será produzido No code object available for <modulename>(veja aqui para mais detalhes).

Comparações detalhadas

Efeitos da execução do módulo via comando python (ie, python <filename>):

  • sys.path é modificado para incluir o diretório final em <filename>
  • __name__ está configurado para '__main__'
  • __package__ está configurado para None
  • __init__.py não é avaliado para nenhum pacote (incluindo o próprio para módulos de pacote)
  • __main__.pyé avaliado para módulos de pacotes; o código é avaliado para módulos de código.

Efeitos da execução do módulo via declaração de importação (ou seja import <modulename>):

  • sys.pathnão é modificado de forma alguma
  • __name__ está definido para a forma absoluta de <modulename>
  • __package__ está definido como o pacote pai imediato em <modulename>
  • __init__.py é avaliado para todos os pacotes (incluindo os próprios para módulos de pacotes)
  • __main__.pynão é avaliado para módulos de pacote; o código é avaliado para módulos de código

Efeitos da execução do módulo via sinalizador -m (ou seja python -m <modulename>):

  • sys.path é modificado para incluir o diretório atual
  • __name__ está configurado para '__main__'
  • __package__ está definido como o pacote pai imediato em <modulename>
  • __init__.py é avaliado para todos os pacotes (incluindo os próprios para módulos de pacotes)
  • __main__.pyé avaliado para módulos de pacotes; o código é avaliado para módulos de código

Conclusão

O -msinalizador é, na sua forma mais simples, um meio de executar scripts python a partir da linha de comando usando nomes de módulos em vez de nomes de arquivos. Além disso, -mfornece funcionalidade adicional que combina o poder das importinstruções (por exemplo, suporte a importações relativas explícitas e __init__avaliação automática de pacotes ) com a conveniência da linha de comando python.


Você também pode adicionar o uso de invocar o pacote usando python -m packagenameo mencionado aqui: stackoverflow.com/a/53772635/1779091
variável

@ boa ideia variável, adicionei uma seção "Caso de uso" que inclui isso.
Mark Rucker
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.