Como adicionar múltiplos argumentos argparse

PythonBeginner
Pratique Agora

Introdução

Este tutorial explora como adicionar e gerenciar múltiplos argumentos de linha de comando em Python usando o módulo argparse. Argumentos de linha de comando permitem que os usuários personalizem como os programas são executados sem modificar o código em si. O módulo argparse simplifica este processo, fornecendo ferramentas para definir, validar e analisar esses argumentos.

Ao final deste tutorial, você entenderá como criar scripts Python que podem aceitar vários parâmetros de entrada, tornando suas aplicações mais flexíveis e fáceis de usar.

Entendendo os Fundamentos do Argparse

O primeiro passo para trabalhar com argumentos de linha de comando é entender os fundamentos do módulo argparse.

O que é Argparse?

Argparse é um módulo Python embutido que facilita a escrita de interfaces de linha de comando amigáveis. Ele gera automaticamente mensagens de ajuda, lida com erros e converte argumentos de linha de comando para os tipos de dados apropriados para o seu programa.

Criando seu Primeiro Script com Argparse

Vamos começar criando um script Python simples que usa argparse. Criaremos um arquivo chamado hello.py que aceita um nome como um argumento de linha de comando.

  1. Abra o editor VSCode e crie um novo arquivo clicando em "File" > "New File"
  2. Salve o arquivo como hello.py no diretório /home/labex/project
  3. Adicione o seguinte código ao arquivo:
import argparse

## Create the parser
parser = argparse.ArgumentParser(description='A simple greeting program')

## Add an argument
parser.add_argument('--name', type=str, default='World', help='Name to greet')

## Parse the arguments
args = parser.parse_args()

## Use the argument
print(f"Hello, {args.name}!")

Este script demonstra as três etapas principais para usar argparse:

  1. Criar um objeto ArgumentParser
  2. Adicionar argumentos ao parser
  3. Analisar os argumentos da linha de comando

Executando seu Script

Vamos executar o script para ver como ele funciona:

  1. Abra um terminal na interface do VSCode (Terminal > New Terminal)
  2. Execute o script com o seguinte comando:
python3 hello.py

Você deve ver esta saída:

Hello, World!

Agora, vamos tentar passar um nome:

python3 hello.py --name Alice

Saída:

Hello, Alice!

Entendendo as Mensagens de Ajuda

Um dos benefícios do argparse é que ele gera automaticamente mensagens de ajuda. Tente executar:

python3 hello.py --help

Você deve ver uma saída semelhante a:

usage: hello.py [-h] [--name NAME]

A simple greeting program

options:
  -h, --help   show this help message and exit
  --name NAME  Name to greet

Esta mensagem de ajuda é gerada automaticamente com base na descrição que fornecemos ao criar o parser e no texto de ajuda que adicionamos ao nosso argumento.

Componentes Chave do Argparse

Vamos examinar os principais componentes em nosso script:

  • parser = argparse.ArgumentParser(description='...'): Cria um parser com uma descrição
  • parser.add_argument('--name', ...): Adiciona um argumento com o nome '--name'
  • type=str: Especifica que o argumento deve ser uma string
  • default='World': Define um valor padrão se o argumento não for fornecido
  • help='Name to greet': Fornece texto de ajuda para o argumento
  • args = parser.parse_args(): Analisa os argumentos da linha de comando
  • args.name: Acessa o valor do argumento 'name'

Agora você entende os fundamentos do uso de argparse para lidar com argumentos de linha de comando em Python.

Adicionando Múltiplos Tipos de Argumentos

Agora que você entende os fundamentos, vamos explorar diferentes tipos de argumentos que podem ser adicionados à sua interface de linha de comando.

Tipos de Argumentos

O Argparse suporta vários tipos de argumentos:

  1. Argumentos posicionais (Positional arguments): Argumentos obrigatórios identificados por sua posição
  2. Argumentos opcionais (Optional arguments): Argumentos precedidos por -- (ou -) que podem ter valores padrão
  3. Argumentos de flag (Flag arguments): Argumentos booleanos que estão presentes ou ausentes
  4. Argumentos com escolhas (Arguments with choices): Argumentos limitados a valores específicos

Vamos criar um novo script que demonstra esses diferentes tipos de argumentos.

  1. Crie um novo arquivo chamado calculator.py no diretório /home/labex/project
  2. Adicione o seguinte código:
import argparse

## Create the parser
parser = argparse.ArgumentParser(description='A simple calculator')

## Add a positional argument
parser.add_argument('operation',
                    choices=['add', 'subtract', 'multiply', 'divide'],
                    help='Math operation to perform')

## Add optional arguments
parser.add_argument('--num1', type=float, required=True,
                    help='First number')
parser.add_argument('--num2', type=float, required=True,
                    help='Second number')

## Add a flag argument
parser.add_argument('--verbose', action='store_true',
                    help='Enable verbose output')

## Parse the arguments
args = parser.parse_args()

## Perform the calculation based on the operation
result = 0
if args.operation == 'add':
    result = args.num1 + args.num2
elif args.operation == 'subtract':
    result = args.num1 - args.num2
elif args.operation == 'multiply':
    result = args.num1 * args.num2
elif args.operation == 'divide':
    if args.num2 == 0:
        print("Error: Cannot divide by zero")
        exit(1)
    result = args.num1 / args.num2

## Display the result
if args.verbose:
    print(f"The result of {args.num1} {args.operation} {args.num2} is: {result}")
else:
    print(f"Result: {result}")

Entendendo os Novos Argumentos

Vamos examinar os novos tipos de argumentos neste script:

  1. Argumento posicional: operation - deve ser um de 'add', 'subtract', 'multiply' ou 'divide'

    parser.add_argument('operation',
                        choices=['add', 'subtract', 'multiply', 'divide'],
                        help='Math operation to perform')
  2. Argumentos opcionais com required=True: --num1 e --num2 - devem ser fornecidos pelo usuário

    parser.add_argument('--num1', type=float, required=True,
                        help='First number')
  3. Argumento de flag: --verbose - quando presente, habilita a saída detalhada

    parser.add_argument('--verbose', action='store_true',
                        help='Enable verbose output')

Testando a Calculadora

Execute a calculadora com diferentes argumentos para ver como ela funciona:

python3 calculator.py add --num1 5 --num2 3

Saída:

Result: 8.0

Tente com a flag verbose:

python3 calculator.py multiply --num1 4 --num2 7 --verbose

Saída:

The result of 4.0 multiply 7.0 is: 28.0

Tente a operação de divisão:

python3 calculator.py divide --num1 10 --num2 2

Saída:

Result: 5.0

Se você esquecer um argumento obrigatório, o argparse mostrará um erro:

python3 calculator.py add --num1 5

Saída:

usage: calculator.py [-h] [--num1 NUM1] [--num2 NUM2] [--verbose]
                    {add,subtract,multiply,divide}
calculator.py: error: the following arguments are required: --num2

Pontos Chave Sobre Tipos de Argumentos

  • Argumentos posicionais não precisam de um prefixo e são identificados por sua posição
  • O parâmetro choices restringe os valores válidos para um argumento
  • O parâmetro required=True torna um argumento opcional obrigatório
  • O parâmetro action='store_true' cria uma flag que é False por padrão e True quando especificada
  • O parâmetro type converte a entrada para o tipo especificado (float em nosso exemplo)

Agora você entende como usar diferentes tipos de argumentos com argparse.

Configurações Avançadas de Argumentos

Agora, vamos explorar configurações mais avançadas para argumentos, incluindo:

  1. Argumentos que aceitam múltiplos valores
  2. Argumentos com validação personalizada
  3. Argumentos com valores padrão
  4. Argumentos com nomes curtos (apelidos de uma letra)

Criando um Script de Processamento de Arquivos

Vamos criar um script que demonstra essas configurações avançadas, construindo um utilitário de processamento de arquivos.

  1. Crie um novo arquivo chamado file_processor.py no diretório /home/labex/project
  2. Adicione o seguinte código:
import argparse
import os

def validate_file(filename):
    """Validate that the file exists."""
    if not os.path.exists(filename):
        raise argparse.ArgumentTypeError(f"File {filename} does not exist")
    return filename

## Create the parser
parser = argparse.ArgumentParser(description='Process text files')

## Multiple values
parser.add_argument('-f', '--files',
                    type=validate_file,
                    nargs='+',
                    help='Input files to process')

## Argument with short name
parser.add_argument('-o', '--output',
                    default='output.txt',
                    help='Output file (default: output.txt)')

## Choices with default
parser.add_argument('-m', '--mode',
                    choices=['read', 'count', 'stats'],
                    default='read',
                    help='Processing mode (default: read)')

## Custom validation for a positive integer
def positive_int(value):
    ivalue = int(value)
    if ivalue <= 0:
        raise argparse.ArgumentTypeError(f"{value} is not a positive integer")
    return ivalue

parser.add_argument('-l', '--lines',
                    type=positive_int,
                    default=10,
                    help='Number of lines to process (default: 10)')

## Parse arguments
args = parser.parse_args()

## Process files based on mode
for file in args.files if args.files else []:
    print(f"Processing file: {file}")

    with open(file, 'r') as f:
        content = f.readlines()

        if args.mode == 'read':
            ## Read mode: output first N lines
            print(f"First {args.lines} lines:")
            for i, line in enumerate(content[:args.lines]):
                print(f"{i+1}: {line.strip()}")

        elif args.mode == 'count':
            ## Count mode: count lines, words, chars
            line_count = len(content)
            word_count = sum(len(line.split()) for line in content)
            char_count = sum(len(line) for line in content)

            print(f"Lines: {line_count}")
            print(f"Words: {word_count}")
            print(f"Characters: {char_count}")

        elif args.mode == 'stats':
            ## Stats mode: line length statistics
            if content:
                line_lengths = [len(line.strip()) for line in content]
                avg_length = sum(line_lengths) / len(line_lengths)
                max_length = max(line_lengths)
                min_length = min(line_lengths)

                print(f"Average line length: {avg_length:.2f}")
                print(f"Shortest line: {min_length} characters")
                print(f"Longest line: {max_length} characters")
            else:
                print("File is empty")

    print("-" * 30)

print(f"Results will be saved to: {args.output}")
## Note: In a real application, we would actually write to the output file

Entendendo as Configurações Avançadas

Vamos examinar as configurações avançadas de argumentos:

  1. Múltiplos valores com nargs='+':

    parser.add_argument('-f', '--files',
                        type=validate_file,
                        nargs='+',
                        help='Input files to process')

    O parâmetro nargs='+' permite que o usuário forneça um ou mais argumentos de arquivo.

  2. Função de validação personalizada:

    def validate_file(filename):
        if not os.path.exists(filename):
            raise argparse.ArgumentTypeError(f"File {filename} does not exist")
        return filename

    Esta função verifica se o arquivo especificado existe antes de prosseguir.

  3. Apelidos curtos com -o:

    parser.add_argument('-o', '--output',
                        default='output.txt',
                        help='Output file (default: output.txt)')

    O -o fornece uma maneira mais curta de especificar o argumento --output.

  4. Escolhas com valor padrão:

    parser.add_argument('-m', '--mode',
                        choices=['read', 'count', 'stats'],
                        default='read',
                        help='Processing mode (default: read)')

    Isso limita o modo a valores específicos, fornecendo um padrão.

Criando Arquivos de Teste de Amostra

Vamos criar dois arquivos de amostra para testar nosso script:

  1. Crie um arquivo de amostra chamado sample1.txt:

    echo -e "This is the first line.\nThis is the second line.\nThis is the third line.\nThis is the fourth line.\nThis is the fifth line." > /home/labex/project/sample1.txt
  2. Crie outro arquivo de amostra chamado sample2.txt:

    echo -e "Lorem ipsum dolor sit amet.\nConsectetur adipiscing elit.\nSed do eiusmod tempor incididunt.\nUt labore et dolore magna aliqua." > /home/labex/project/sample2.txt

Testando o Processador de Arquivos

Agora, vamos executar o script com diferentes argumentos:

  1. Uso básico com um arquivo:

    python3 file_processor.py -f sample1.txt

    Saída:

    Processing file: sample1.txt
    First 10 lines:
    1: This is the first line.
    2: This is the second line.
    3: This is the third line.
    4: This is the fourth line.
    5: This is the fifth line.
    ------------------------------
    Results will be saved to: output.txt
  2. Processar múltiplos arquivos:

    python3 file_processor.py -f sample1.txt sample2.txt

    Saída:

    Processing file: sample1.txt
    First 10 lines:
    1: This is the first line.
    2: This is the second line.
    3: This is the third line.
    4: This is the fourth line.
    5: This is the fifth line.
    ------------------------------
    Processing file: sample2.txt
    First 10 lines:
    1: Lorem ipsum dolor sit amet.
    2: Consectetur adipiscing elit.
    3: Sed do eiusmod tempor incididunt.
    4: Ut labore et dolore magna aliqua.
    ------------------------------
    Results will be saved to: output.txt
  3. Usar o modo count:

    python3 file_processor.py -f sample1.txt -m count

    Saída:

    Processing file: sample1.txt
    Lines: 5
    Words: 25
    Characters: 135
    ------------------------------
    Results will be saved to: output.txt
  4. Usar o modo stats:

    python3 file_processor.py -f sample2.txt -m stats

    Saída:

    Processing file: sample2.txt
    Average line length: 29.25
    Shortest line: 22 characters
    Longest line: 37 characters
    ------------------------------
    Results will be saved to: output.txt
  5. Limitar o número de linhas:

    python3 file_processor.py -f sample1.txt -l 2

    Saída:

    Processing file: sample1.txt
    First 2 lines:
    1: This is the first line.
    2: This is the second line.
    ------------------------------
    Results will be saved to: output.txt

Pontos Chave Sobre Configurações Avançadas

  • nargs='+' permite múltiplos valores de argumento
  • Funções de validação personalizadas ajudam a garantir uma entrada válida
  • Nomes de argumentos curtos (apelidos de uma letra) fornecem conveniência
  • Valores padrão simplificam o uso para cenários comuns
  • O parâmetro choices restringe a entrada a opções válidas

Agora você aprendeu como configurar opções avançadas de argumentos em seus scripts de linha de comando.

Construindo uma Ferramenta de Linha de Comando Completa

Nesta etapa, vamos combinar tudo o que aprendemos para construir uma ferramenta de linha de comando completa. Vamos criar um gerenciador de tarefas simples que permite aos usuários adicionar, listar e excluir tarefas usando argumentos de linha de comando.

Criando o Gerenciador de Tarefas

  1. Crie um novo arquivo chamado task_manager.py no diretório /home/labex/project
  2. Adicione o seguinte código:
import argparse
import json
import os

## File to store tasks
TASKS_FILE = "/home/labex/project/tasks.json"

def load_tasks():
    """Load tasks from the JSON file."""
    if os.path.exists(TASKS_FILE):
        with open(TASKS_FILE, 'r') as f:
            try:
                return json.load(f)
            except json.JSONDecodeError:
                return []
    return []

def save_tasks(tasks):
    """Save tasks to the JSON file."""
    with open(TASKS_FILE, 'w') as f:
        json.dump(tasks, f, indent=2)

def main():
    ## Create the main parser
    parser = argparse.ArgumentParser(description='Task Manager CLI')
    subparsers = parser.add_subparsers(dest='command', help='Command to run')

    ## Add command
    add_parser = subparsers.add_parser('add', help='Add a new task')
    add_parser.add_argument('title', help='Task title')
    add_parser.add_argument('-p', '--priority',
                          choices=['low', 'medium', 'high'],
                          default='medium',
                          help='Task priority (default: medium)')
    add_parser.add_argument('-d', '--due',
                          help='Due date (format: YYYY-MM-DD)')

    ## List command
    list_parser = subparsers.add_parser('list', help='List all tasks')
    list_parser.add_argument('-p', '--priority',
                           choices=['low', 'medium', 'high'],
                           help='Filter tasks by priority')
    list_parser.add_argument('-s', '--sort',
                           choices=['priority', 'title'],
                           default='priority',
                           help='Sort tasks by criteria (default: priority)')

    ## Delete command
    delete_parser = subparsers.add_parser('delete', help='Delete a task')
    delete_parser.add_argument('task_id', type=int, help='Task ID to delete')

    ## Parse arguments
    args = parser.parse_args()

    ## Load existing tasks
    tasks = load_tasks()

    ## Handle commands
    if args.command == 'add':
        ## Add a new task
        new_task = {
            'id': len(tasks) + 1,
            'title': args.title,
            'priority': args.priority,
            'due': args.due
        }
        tasks.append(new_task)
        save_tasks(tasks)
        print(f"Task added: {new_task['title']} (ID: {new_task['id']})")

    elif args.command == 'list':
        ## List tasks
        if not tasks:
            print("No tasks found.")
            return

        ## Filter by priority if specified
        if args.priority:
            filtered_tasks = [t for t in tasks if t['priority'] == args.priority]
        else:
            filtered_tasks = tasks

        ## Sort tasks
        if args.sort == 'priority':
            ## Custom priority sorting
            priority_order = {'high': 0, 'medium': 1, 'low': 2}
            sorted_tasks = sorted(filtered_tasks, key=lambda x: priority_order[x['priority']])
        else:
            ## Sort by title
            sorted_tasks = sorted(filtered_tasks, key=lambda x: x['title'])

        ## Display tasks
        print("ID | Title                 | Priority | Due Date")
        print("-" * 50)
        for task in sorted_tasks:
            due_date = task['due'] if task['due'] else 'N/A'
            print(f"{task['id']:2} | {task['title'][:20]:<20} | {task['priority']:<8} | {due_date}")

    elif args.command == 'delete':
        ## Delete a task
        task_id = args.task_id
        task_found = False

        for i, task in enumerate(tasks):
            if task['id'] == task_id:
                del tasks[i]
                task_found = True
                break

        if task_found:
            save_tasks(tasks)
            print(f"Task {task_id} deleted.")
        else:
            print(f"Task {task_id} not found.")

    else:
        ## No command specified
        parser.print_help()

if __name__ == "__main__":
    main()

Entendendo o Gerenciador de Tarefas

Este script demonstra vários recursos avançados do argparse:

  1. Subparsers - Criando conjuntos de argumentos específicos para comandos
  2. Argumentos específicos para comandos - Diferentes argumentos para os comandos add, list e delete
  3. Validação aninhada - Escolhas de prioridade limitadas a valores específicos
  4. Valores padrão - Fornecendo padrões sensíveis para argumentos opcionais

Testando o Gerenciador de Tarefas

Vamos executar o script com diferentes comandos para ver como ele funciona:

  1. Adicionando tarefas:

    python3 task_manager.py add "Complete Python tutorial" -p high -d 2023-12-31

    Saída:

    Task added: Complete Python tutorial (ID: 1)

    Adicione mais algumas tarefas:

    python3 task_manager.py add "Read documentation" -p medium
    python3 task_manager.py add "Take a break" -p low -d 2023-12-25
  2. Listando tarefas:

    python3 task_manager.py list

    Saída:

    ID | Title                 | Priority | Due Date
    --------------------------------------------------
    1  | Complete Python tutor | high     | 2023-12-31
    2  | Read documentation    | medium   | N/A
    3  | Take a break          | low      | 2023-12-25
  3. Listando tarefas com filtragem e ordenação:

    python3 task_manager.py list -s title

    Saída:

    ID | Title                 | Priority | Due Date
    --------------------------------------------------
    1  | Complete Python tutor | high     | 2023-12-31
    2  | Read documentation    | medium   | N/A
    3  | Take a break          | low      | 2023-12-25
  4. Filtrando por prioridade:

    python3 task_manager.py list -p high

    Saída:

    ID | Title                 | Priority | Due Date
    --------------------------------------------------
    1  | Complete Python tutor | high     | 2023-12-31
  5. Excluindo uma tarefa:

    python3 task_manager.py delete 2

    Saída:

    Task 2 deleted.

    Verifique se a tarefa foi excluída:

    python3 task_manager.py list

    Saída:

    ID | Title                 | Priority | Due Date
    --------------------------------------------------
    1  | Complete Python tutor | high     | 2023-12-31
    3  | Take a break          | low      | 2023-12-25

Obtendo Ajuda para Subcomandos

O Argparse gera automaticamente ajuda para cada subcomando:

python3 task_manager.py add --help

Saída:

usage: task_manager.py add [-h] [-p {low,medium,high}] [-d DUE] title

positional arguments:
  title                 Task title

options:
  -h, --help            show this help message and exit
  -p {low,medium,high}, --priority {low,medium,high}
                        Task priority (default: medium)
  -d DUE, --due DUE     Due date (format: YYYY-MM-DD)

Pontos Chave Sobre Subcomandos

  • Subcomandos criam conjuntos de argumentos específicos para comandos
  • Cada subcomando pode ter seus próprios argumentos
  • O parâmetro dest especifica onde o nome do comando será armazenado
  • Mensagens de ajuda são geradas automaticamente para cada subcomando
  • Você pode misturar argumentos posicionais e opcionais em subparsers

Agora você tem uma ferramenta de linha de comando totalmente funcional que demonstra o poder e a flexibilidade do argparse para lidar com cenários de argumentos complexos.

Resumo

Neste tutorial, você aprendeu como usar o módulo argparse do Python para lidar com argumentos de linha de comando de forma eficaz. Você explorou:

  • Funcionalidade básica do argparse e criação de scripts simples
  • Diferentes tipos de argumentos: posicionais, opcionais, flags e escolhas
  • Configurações avançadas de argumentos, como múltiplos valores e validação personalizada
  • Construindo uma ferramenta de linha de comando completa com subcomandos

Essas habilidades permitem que você crie aplicações Python mais flexíveis e fáceis de usar, que podem ser configuradas por meio de argumentos de linha de comando sem modificar o código.

A análise de argumentos de linha de comando é uma habilidade essencial para desenvolvedores Python que desejam construir ferramentas e utilitários de qualidade profissional. O módulo argparse fornece uma maneira robusta e padronizada de implementar essa funcionalidade em seus scripts.

Agora você pode aplicar essas técnicas aos seus próprios projetos Python para torná-los mais versáteis e acessíveis aos usuários.