如何添加多个 argparse 参数

PythonBeginner
立即练习

介绍

本教程探讨了如何使用 argparse 模块在 Python 中添加和管理多个命令行参数。命令行参数允许用户自定义程序的运行方式,而无需修改代码本身。argparse 模块通过提供定义、验证和解析这些参数的工具来简化此过程。

通过本教程,你将了解如何创建可以接受各种输入参数的 Python 脚本,从而使你的应用程序更灵活、更用户友好。

理解 Argparse 的基础知识

使用命令行参数的第一步是理解 argparse 模块的基础知识。

什么是 Argparse?

Argparse 是一个内置的 Python 模块,它使编写用户友好的命令行界面变得容易。它会自动生成帮助消息,处理错误,并将命令行参数转换为程序适当的数据类型。

使用 Argparse 创建你的第一个脚本

让我们从创建一个使用 argparse 的简单 Python 脚本开始。我们将创建一个名为 hello.py 的文件,该文件接受一个名称作为命令行参数。

  1. 打开 VSCode 编辑器,通过点击 "File" > "New File" 创建一个新文件
  2. 将文件保存为 /home/labex/project 目录下的 hello.py
  3. 将以下代码添加到文件中:
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}!")

此脚本演示了使用 argparse 的三个主要步骤:

  1. 创建 ArgumentParser 对象
  2. 向解析器添加参数
  3. 解析命令行参数

运行你的脚本

让我们运行脚本,看看它是如何工作的:

  1. 在 VSCode 界面中打开一个终端(Terminal > New Terminal)
  2. 使用以下命令运行脚本:
python3 hello.py

你应该看到以下输出:

Hello, World!

现在让我们尝试传递一个名称:

python3 hello.py --name Alice

输出:

Hello, Alice!

理解帮助消息

argparse 的好处之一是它会自动生成帮助消息。尝试运行:

python3 hello.py --help

你应该看到类似于以下的输出:

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

A simple greeting program

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

此帮助消息是根据我们在创建解析器时提供的描述以及我们添加到参数中的帮助文本自动生成的。

Argparse 的关键组件

让我们检查一下脚本中的主要组件:

  • parser = argparse.ArgumentParser(description='...'):创建一个带有描述的解析器
  • parser.add_argument('--name', ...):添加一个名为 '--name' 的参数
  • type=str:指定参数应该是一个字符串
  • default='World':如果未提供参数,则设置一个默认值
  • help='Name to greet':为参数提供帮助文本
  • args = parser.parse_args():解析命令行参数
  • args.name:访问 'name' 参数的值

现在你了解了使用 argparse 在 Python 中处理命令行参数的基础知识。

添加多种参数类型

现在你已经了解了基础知识,让我们探索可以添加到命令行界面的不同类型的参数。

参数类型

Argparse 支持几种类型的参数:

  1. 位置参数(Positional arguments):通过它们的位置标识的必需参数
  2. 可选参数(Optional arguments):以 --(或 -)为前缀的参数,可能具有默认值
  3. 标志参数(Flag arguments):存在或不存在的布尔参数
  4. 带有选择的参数(Arguments with choices):仅限于特定值的参数

让我们创建一个新脚本来演示这些不同的参数类型。

  1. /home/labex/project 目录中创建一个名为 calculator.py 的新文件
  2. 添加以下代码:
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}")

理解新参数

让我们检查一下此脚本中的新参数类型:

  1. 位置参数(Positional argument)operation - 必须是 'add'、'subtract'、'multiply' 或 'divide' 之一

    parser.add_argument('operation',
                        choices=['add', 'subtract', 'multiply', 'divide'],
                        help='Math operation to perform')
  2. 带有 required=True 的可选参数(Optional arguments with required=True)--num1--num2 - 必须由用户提供

    parser.add_argument('--num1', type=float, required=True,
                        help='First number')
  3. 标志参数(Flag argument)--verbose - 存在时,启用详细输出

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

测试计算器

使用不同的参数运行计算器,看看它是如何工作的:

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

输出:

Result: 8.0

尝试使用 verbose 标志:

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

输出:

The result of 4.0 multiply 7.0 is: 28.0

尝试除法运算:

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

输出:

Result: 5.0

如果你忘记了必需的参数,argparse 将显示一个错误:

python3 calculator.py add --num1 5

输出:

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

关于参数类型的关键点

  • 位置参数(Positional arguments) 不需要前缀,并通过它们的位置来标识
  • choices 参数限制了参数的有效值
  • required=True 参数使可选参数成为必需参数
  • action='store_true' 参数创建一个默认值为 False 并在指定时为 True 的标志
  • type 参数将输入转换为指定的类型(在我们的示例中为 float)

现在你了解了如何使用不同类型的参数与 argparse。

高级参数配置

现在让我们探索更高级的参数配置,包括:

  1. 接受多个值的参数
  2. 具有自定义验证的参数
  3. 具有默认值的参数
  4. 具有短名称(单字母别名)的参数

创建一个文件处理脚本

让我们创建一个脚本,通过构建一个文件处理实用程序来演示这些高级配置。

  1. /home/labex/project 目录中创建一个名为 file_processor.py 的新文件
  2. 添加以下代码:
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

理解高级配置

让我们检查一下高级参数配置:

  1. 使用 nargs='+' 的多个值

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

    nargs='+' 参数允许用户提供一个或多个文件参数。

  2. 自定义验证函数

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

    此函数在继续之前检查指定的文件是否存在。

  3. 使用 -o 的短别名

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

    -o 提供了一种更短的方式来指定 --output 参数。

  4. 带有默认值的选择

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

    这限制了模式为特定值,同时提供了一个默认值。

创建示例测试文件

让我们创建两个示例文件来测试我们的脚本:

  1. 创建一个名为 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. 创建另一个名为 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

测试文件处理器

现在让我们使用不同的参数运行脚本:

  1. 基本用法,使用一个文件:

    python3 file_processor.py -f sample1.txt

    输出:

    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. 处理多个文件:

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

    输出:

    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. 使用 count 模式:

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

    输出:

    Processing file: sample1.txt
    Lines: 5
    Words: 25
    Characters: 135
    ------------------------------
    Results will be saved to: output.txt
  4. 使用 stats 模式:

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

    输出:

    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. 限制行数:

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

    输出:

    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

关于高级配置的关键点

  • nargs='+' 允许使用多个参数值
  • 自定义验证函数有助于确保输入有效
  • 短参数名称(单字母别名)提供了便利
  • 默认值简化了常见场景的使用
  • choices 参数将输入限制为有效选项

现在你已经学会了如何在命令行脚本中配置高级参数选项。

构建一个完整的命令行工具

在这一步,我们将结合我们所学的一切来构建一个完整的命令行工具。让我们创建一个简单的任务管理器,允许用户使用命令行参数添加、列出和删除任务。

创建任务管理器

  1. /home/labex/project 目录中创建一个名为 task_manager.py 的新文件
  2. 添加以下代码:
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()

理解任务管理器

此脚本演示了 argparse 的几个高级功能:

  1. 子解析器(Subparsers) - 创建特定于命令的参数集
  2. 特定于命令的参数(Command-specific arguments) - 用于添加、列出和删除命令的不同参数
  3. 嵌套验证(Nested validation) - 优先级选择仅限于特定值
  4. 默认值(Default values) - 为可选参数提供合理的默认值

测试任务管理器

让我们使用不同的命令运行脚本,看看它是如何工作的:

  1. 添加任务:

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

    输出:

    Task added: Complete Python tutorial (ID: 1)

    再添加几个任务:

    python3 task_manager.py add "Read documentation" -p medium
    python3 task_manager.py add "Take a break" -p low -d 2023-12-25
  2. 列出任务:

    python3 task_manager.py list

    输出:

    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. 列出任务,并进行过滤和排序:

    python3 task_manager.py list -s title

    输出:

    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. 按优先级过滤:

    python3 task_manager.py list -p high

    输出:

    ID | Title                 | Priority | Due Date
    --------------------------------------------------
    1  | Complete Python tutor | high     | 2023-12-31
  5. 删除任务:

    python3 task_manager.py delete 2

    输出:

    Task 2 deleted.

    验证任务是否被删除:

    python3 task_manager.py list

    输出:

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

获取子命令的帮助

Argparse 会自动为每个子命令生成帮助:

python3 task_manager.py add --help

输出:

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)

关于子解析器的关键点

  • 子解析器创建特定于命令的参数集
  • 每个子解析器可以有自己的参数
  • dest 参数指定命令名称的存储位置
  • 将自动为每个子命令生成帮助消息
  • 你可以在子解析器中混合使用位置参数和可选参数

你现在拥有一个功能齐全的命令行工具,它演示了 argparse 在处理复杂参数场景方面的强大功能和灵活性。

总结

在本教程中,你学习了如何使用 Python 的 argparse 模块来有效地处理命令行参数。你探索了:

  • 基本的 argparse 功能和创建简单的脚本
  • 不同类型的参数:位置参数、可选参数、标志和选择
  • 高级参数配置,如多个值和自定义验证
  • 使用子命令构建一个完整的命令行工具

这些技能使你能够创建更灵活、用户友好的 Python 应用程序,这些应用程序可以通过命令行参数进行配置,而无需修改代码。

对于希望构建专业质量的工具和实用程序的 Python 开发人员来说,命令行参数解析是一项基本技能。argparse 模块为在你的脚本中实现此功能提供了一种强大且标准化的方法。

现在,你可以将这些技术应用于你自己的 Python 项目,使它们对用户更具通用性和可访问性。