Exemplo simples de argparse: 1 argumento, 3 resultados

529

A documentação para o módulo python argparse , embora excelente, tenho certeza, é demais para o meu pequeno cérebro iniciante entender agora. Não preciso fazer contas na linha de comando nem interferir com as linhas de formatação na tela ou alterar os caracteres das opções. Tudo o que eu quero fazer é "Se arg é A, faça isso, se B fizer isso, se nenhuma das opções acima mostrar ajuda e sair" .

Matt Wilson
fonte
15
em seguida, basta verificar sys.argvpara o argumento que quiser ...
JBernardo
10
Já tentou plac ? É um invólucro fácil de usar sobre argparse com ótima documentação .
kirbyfan64sos
157
não é você. é argparse. está tentando levá-lo a uma jornada para as estrelas e não se importa para onde você estava indo.
Florian Heigl
11
APIs "
pitonicas
69
Abençoe você, Matt Wilkie, por defender minúsculos cérebros iniciantes em todos os lugares.
polka

Respostas:

255

Minha compreensão da pergunta original é dupla. Primeiro, em termos do exemplo de argumento mais simples possível, estou surpreso por não ter visto aqui. É claro que, para simplificar, também está tudo sobrecarregado com pouco poder, mas pode ajudar você a começar.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("a")
args = parser.parse_args()

if args.a == 'magic.name':
    print 'You nailed it!'

Mas esse argumento posicional é agora necessário. Se você deixar de fora ao chamar este programa, receberá um erro sobre a falta de argumentos. Isso me leva à segunda parte da pergunta original. Matt Wilkie parece querer um único argumento opcional sem um rótulo nomeado (os rótulos --option). Minha sugestão seria modificar o código acima da seguinte maneira:

...
parser.add_argument("a", nargs='?', default="check_string_for_empty")
...
if args.a == 'check_string_for_empty':
    print 'I can tell that no argument was given and I can deal with that here.'
elif args.a == 'magic.name':
    print 'You nailed it!'
else:
    print args.a

Pode muito bem haver uma solução mais elegante, mas isso funciona e é minimalista.

mightypile
fonte
4
Depois de algum tempo refletindo, concluo que essa pergunta realmente responde melhor ao Q conforme solicitado e à situação em que eu estava naquele tempo. As outras excelentes respostas ganharam mais do que suficiente representante para provar o seu valor e podem suportar um pouco de concorrência. :-) #
11265 matt wilkie #:
@badnack: É o que você quer que seja, o que 'a' representa. Se você espera um argumento, um nome de arquivo, por exemplo, é o que foi inserido como o nome do arquivo na linha de comando. Você pode então fazer seu próprio processamento para determinar se ele existe no sistema de arquivos, mas essa é outra pergunta e resposta.
Mightypile
363

Aqui está a maneira como eu faço isso argparse(com vários argumentos):

parser = argparse.ArgumentParser(description='Description of your program')
parser.add_argument('-f','--foo', help='Description for foo argument', required=True)
parser.add_argument('-b','--bar', help='Description for bar argument', required=True)
args = vars(parser.parse_args())

args será um dicionário que contém os argumentos:

if args['foo'] == 'Hello':
    # code here

if args['bar'] == 'World':
    # code here

No seu caso, basta adicionar apenas um argumento.

Diego Navarro
fonte
3
como mencionado no meu comentário para a outra resposta, eu gostaria de manter a formatação automática da ajuda do argparse, mas não parece haver uma opção para ter um argumento sem nome (mais provavelmente eu simplesmente não entendo quando o vejo) ), por exemplo, uma necessidade de fazer foo.py --action installou foo.py --action removeem vez de simplesmentefoo.py install
mate Wilkie
7
@mattwilkie Então você precisa definir um argumento posicional como este: parser.add_argument('install', help='Install the app') (Observe que você não pode definir um argumento posicional required=True)
Diego Navarro
32
Como noob para argumentar, essa resposta realmente ajudou porque eu não sabia onde encontrar as opções depois que elas foram aprovadas . Em outras palavras, eu precisava entender como o argsditado foi gerado como acima.
mrKelley
3
Use o 'formulário curto' ao chamar o programa diretamente da linha de comando e o 'formulário longo' ao executar um programa / comando em um script. Nesse caso, é mais legível por humanos com a forma longa e, portanto, mais fácil de seguir a lógica do código / script.
ola
17
Pessoalmente, acho mais fácil acessar argumentos como args.fooe em args.barvez da sintaxe do dicionário. De qualquer maneira, é claro, mas args não é realmente um dicionário, mas um argparse.Namespaceobjeto.
Michael Mior
210

A argparsedocumentação é razoavelmente boa, mas deixa de fora alguns detalhes úteis que podem não ser óbvios. (@ Diego Navarro já mencionou um pouco disso, mas tentarei expandir um pouco sua resposta.) O uso básico é o seguinte:

parser = argparse.ArgumentParser()
parser.add_argument('-f', '--my-foo', default='foobar')
parser.add_argument('-b', '--bar-value', default=3.14)
args = parser.parse_args()

O objeto do qual você volta parse_args() é um objeto 'Namespace': um objeto cujas variáveis ​​de membro são nomeadas após os argumentos da linha de comando. O Namespaceobjetivo é como você acessa seus argumentos e os valores associados a eles:

args = parser.parse_args()
print args.my_foo
print args.bar_value

(Observe que argparsesubstitui '-' nos nomes dos argumentos por sublinhados ao nomear as variáveis.)

Em muitas situações, você pode querer usar argumentos simplesmente como sinalizadores que não têm valor. Você pode adicionar aqueles no argparse assim:

parser.add_argument('--foo', action='store_true')
parser.add_argument('--no-foo', action='store_false')

O exemplo acima criará variáveis ​​nomeadas 'foo' com o valor True e 'no_foo' com o valor False, respectivamente:

if (args.foo):
    print "foo is true"

if (args.no_foo is False):
    print "nofoo is false"

Observe também que você pode usar a opção "obrigatório" ao adicionar um argumento:

parser.add_argument('-o', '--output', required=True)

Dessa forma, se você omitir esse argumento na linha de comando argparse , ele faltará e interromperá a execução do seu script.

Por fim, observe que é possível criar uma estrutura de ditado para seus argumentos usando a varsfunção, se isso facilitar a vida para você.

args = parser.parse_args()
argsdict = vars(args)
print argsdict['my_foo']
print argsdict['bar_value']

Como você pode ver, vars retorna um ditado com seus nomes de argumento como chaves e seus valores como, er, valores.

Existem muitas outras opções e coisas que você pode fazer, mas isso deve abranger os cenários de uso comuns mais essenciais.

DMH
fonte
3
Qual é o sentido do '-f'e '-b'? Por que você não pode omitir isso?
usar o seguinte comando
13
É bastante convencional ter as versões 'short form' (um traço) e 'long form' (dois traços) para cada opção de tempo de execução. Você verá isso, por exemplo, em quase todos os utilitários Unix / Linux padrão; faça um man cpou man lse você encontrará muitas opções nos dois sabores (por exemplo -f, --force). Provavelmente, existem várias razões pelas quais as pessoas preferem um ou outro, mas, em qualquer caso, é bastante padrão disponibilizar os dois formulários no seu programa.
DMH
59

Matt está perguntando sobre parâmetros posicionais em argparse, e eu concordo que a documentação do Python está ausente nesse aspecto. Não há um exemplo completo e único nas ~ 20 páginas ímpares que mostram a análise e o uso de parâmetros posicionais .

Nenhuma das outras respostas aqui mostra um exemplo completo de parâmetros posicionais, então aqui está um exemplo completo:

# tested with python 2.7.1
import argparse

parser = argparse.ArgumentParser(description="An argparse example")

parser.add_argument('action', help='The action to take (e.g. install, remove, etc.)')
parser.add_argument('foo-bar', help='Hyphens are cumbersome in positional arguments')

args = parser.parse_args()

if args.action == "install":
    print("You asked for installation")
else:
    print("You asked for something other than installation")

# The following do not work:
# print(args.foo-bar)
# print(args.foo_bar)

# But this works:
print(getattr(args, 'foo-bar'))

O que me impressionou é que o argparse converterá o argumento nomeado "--foo-bar" em "foo_bar", mas um parâmetro posicional chamado "foo-bar" permanece como "foo-bar", tornando menos óbvio como use-o no seu programa.

Observe as duas linhas próximas ao final do meu exemplo - nenhuma delas funcionará para obter o valor do parâmetro posicional foo-bar. O primeiro está obviamente errado (é uma expressão aritmética args.foo menos barra), mas o segundo também não funciona:

AttributeError: 'Namespace' object has no attribute 'foo_bar'

Se você deseja usar o foo-baratributo, deve usá-lo getattr, como visto na última linha do meu exemplo. O que é louco é que, se você tentasse dest=foo_baralterar o nome da propriedade para algo mais fácil de acessar, receberia uma mensagem de erro realmente bizarra:

ValueError: dest supplied twice for positional argument

Veja como o exemplo acima é executado:

$ python test.py
usage: test.py [-h] action foo-bar
test.py: error: too few arguments

$ python test.py -h
usage: test.py [-h] action foo-bar

An argparse example

positional arguments:
  action      The action to take (e.g. install, remove, etc.)
  foo-bar     Hyphens are cumbersome in positional arguments

optional arguments:
  -h, --help  show this help message and exit

$ python test.py install foo
You asked for installation
foo
Mark E. Haase
fonte
5
nargs='?'é o encantamento para uma "posição opcional", conforme stackoverflow.com/questions/4480075/… #
MarkHu 23/10/12
O fato de uma posição foo-barnão ser transformada foo_baré abordada em bugs.python.org/issue15125 .
hpaulj
2
Eu acho que uma solução mais fácil para esse bug é chamar o argumento "foo_bar" em vez de "foo-bar", e depois print args.foo_barfunciona. Como é um argumento posicional, você não precisa especificar o nome ao chamar o script, portanto, isso não importa para o usuário.
luator 27/08/2015
@luator Você está certo, é fácil renomear o argumento, mas o autor do relatório de erros argumenta que isso ainda é uma característica incorreta devido à carga cognitiva desnecessária. Ao usar argparse, é necessário pausar e recuperar as diferentes convenções de nomenclatura para opções e argumentos. Veja bugs.python.org/msg164968 .
Mark E. Haase
1
@ mehaase Concordo plenamente que esta é uma falha de funcionalidade que deve ser corrigida. Apenas acho que renomear o argumento é a solução mais fácil e menos confusa do que ter que usar getattr(também é mais flexível, pois permite alterar um argumento de opcional para posicional sem precisar alterar o código que usa o valor).
luator 28/08/2015
22

Mais uma introdução resumida, inspirada neste post .

import argparse

# define functions, classes, etc.

# executes when your script is called from the command-line
if __name__ == "__main__":

    parser = argparse.ArgumentParser()
    #
    # define each option with: parser.add_argument
    #
    args = parser.parse_args() # automatically looks at sys.argv
    #
    # access results with: args.argumentName
    #

Os argumentos são definidos com combinações do seguinte:

parser.add_argument( 'name', options... )              # positional argument
parser.add_argument( '-x', options... )                # single-char flag
parser.add_argument( '-x', '--long-name', options... ) # flag with long name

As opções comuns são:

  • ajuda : descrição para este argumento quando--help é usado.
  • padrão : valor padrão se o argumento for omitido.
  • tipo : se você espera um floatou int(caso contrário, éstr ).
  • dest : dê um nome diferente para uma bandeira (por exemplo '-x', '--long-name', dest='longName').
    Nota: por padrão, --long-nameé acessado comargs.long_name
  • ação : para tratamento especial de certos argumentos
    • store_true, store_false: para args booleanos
      '--foo', action='store_true' => args.foo == True
    • store_const: para ser usado com a opçãoconst
      '--foo', action='store_const', const=42 => args.foo == 42
    • count: para opções repetidas, como em./myscript.py -vv
      '-v', action='count' => args.v == 2
    • append: para opções repetidas, como em./myscript.py --foo 1 --foo 2
      '--foo', action='append' => args.foo == ['1', '2']
  • requeridos : se um sinalizador é necessário ou um argumento posicional não é.
  • nargs : para uma bandeira capturar N args
    ./myscript.py --foo a b => args.foo = ['a', 'b']
  • opções : para restringir possíveis entradas (especifique como lista de strings, ou ints se type=int).
Jonathan H
fonte
12

Observe o Tutorial Argparse nos HOWTOs do Python . Começa a partir dos exemplos mais básicos, como este:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)

e progride para menos básicos.

Há um exemplo com escolha predefinida para uma opção, como o que é perguntado:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)
Alexey
fonte
É bom ver que os documentos foram atualizados. Garanto que esse não foi o caso quando a OP postou a pergunta há 5 anos.
Ntwrkguru
10

Aqui está o que eu criei no meu projeto de aprendizagem, graças principalmente a @DMH ...

Código de demonstração:

import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-f', '--flag', action='store_true', default=False)  # can 'store_false' for no-xxx flags
    parser.add_argument('-r', '--reqd', required=True)
    parser.add_argument('-o', '--opt', default='fallback')
    parser.add_argument('arg', nargs='*') # use '+' for 1 or more args (instead of 0 or more)
    parsed = parser.parse_args()
    # NOTE: args with '-' have it replaced with '_'
    print('Result:',  vars(parsed))
    print('parsed.reqd:', parsed.reqd)

if __name__ == "__main__":
    main()

Isso pode ter evoluído e está disponível online: command-line.py

Script para treinar este código: command-line-demo.sh

Peter L
fonte
2
Finalmente um exemplo argparse que faz sentido
opentokix
5

Você também pode usar plac (um invólucro em torno deargparse ).

Como um bônus, ele gera instruções simples de ajuda - veja abaixo.

Script de exemplo:

#!/usr/bin/env python3
def main(
    arg: ('Argument with two possible values', 'positional', None, None, ['A', 'B'])
):
    """General help for application"""
    if arg == 'A':
        print("Argument has value A")
    elif arg == 'B':
        print("Argument has value B")

if __name__ == '__main__':
    import plac
    plac.call(main)

Exemplo de saída:

Nenhum argumento fornecido - example.py :

usage: example.py [-h] {A,B}
example.py: error: the following arguments are required: arg

Argumento inesperado fornecido - example.py C :

usage: example.py [-h] {A,B}
example.py: error: argument arg: invalid choice: 'C' (choose from 'A', 'B')

Argumento correto fornecido - example.py A :

Argument has value A

Menu de ajuda completo (gerado automaticamente) - example.py -h :

usage: example.py [-h] {A,B}

General help for application

positional arguments:
  {A,B}       Argument with two possible values

optional arguments:
  -h, --help  show this help message and exit

Breve explicação:

O nome do argumento geralmente é igual ao nome do parâmetro (arg ).

A anotação de tupla após o argparâmetro tem o seguinte significado:

  • Descrição ( Argument with two possible values)
  • Tipo de argumento - um de 'sinalizador', 'opção' ou 'posicional' ( positional)
  • Abreviação ( None)
  • Tipo de valor do argumento - por exemplo. flutuante, string ( None)
  • Conjunto restrito de opções ( ['A', 'B'])

Documentação:

Para saber mais sobre o uso do plac, confira a excelente documentação:

Plac: Analisando a linha de comando da maneira mais fácil

quase
fonte
4

Para adicionar ao que outros declararam:

Eu geralmente gosto de usar o parâmetro 'dest' para especificar um nome de variável e, em seguida, usar 'globals (). Update ()' para colocar essas variáveis ​​no espaço de nomes global.

Uso:

$ python script.py -i "Hello, World!"

Código:

...
parser.add_argument('-i', '--input', ..., dest='inputted_variable',...)
globals().update(vars(parser.parse_args()))
...
print(inputted_variable) # Prints "Hello, World!"
Sandy Chapman
fonte
argparseUsa internamente getattre setattrpara acessar valores no Namespace. Dessa forma, não é incomodado por destvalores estranhamente formados .
hpaulj
1

Uma maneira realmente simples de usar argparse e alterar as opções '-h' / '--help' para exibir suas próprias instruções de ajuda de código pessoal é definir a ajuda padrão como False; você também pode adicionar quantos .add_arguments adicionais desejar :

import argparse

parser = argparse.ArgumentParser(add_help=False)

parser.add_argument('-h', '--help', action='help',
                help='To run this script please provide two arguments')
parser.parse_args()

Execute: python test.py -h

Resultado:

usage: test.py [-h]

optional arguments:
  -h, --help  To run this script please provide two arguments
Hutch
fonte
-1

A resposta mais simples!

PS quem escreveu o documento de argparse é tolo

código python:

import argparse
parser = argparse.ArgumentParser(description='')
parser.add_argument('--o_dct_fname',type=str)
parser.add_argument('--tp',type=str)
parser.add_argument('--new_res_set',type=int)
args = parser.parse_args()
o_dct_fname = args.o_dct_fname
tp = args.tp
new_res_set = args.new_res_set

código em execução

python produce_result.py --o_dct_fname o_dct --tp father_child --new_res_set 1
gouxute
fonte
Esta resposta não adiciona nada de novo / diferente das respostas existentes.
NVS Abhilash
eu sou o mais claro, a conversa é barata, mostre o código
gouxute 30/04