Python argparse で複数の引数を追加する方法

PythonBeginner
オンラインで実践に進む

はじめに

このチュートリアルでは、Python の argparse モジュールを使用して、複数のコマンドライン引数を追加および管理する方法について解説します。コマンドライン引数を使用すると、ユーザーはコード自体を変更することなく、プログラムの実行方法をカスタマイズできます。argparse モジュールは、これらの引数を定義、検証、および解析するためのツールを提供することにより、このプロセスを簡素化します。

このチュートリアルを終える頃には、さまざまな入力パラメータを受け入れることができる Python スクリプトを作成する方法を理解し、アプリケーションをより柔軟で使いやすくすることができます。

Argparse の基本を理解する

コマンドライン引数を扱う最初のステップは、argparse モジュールの基本を理解することです。

Argparse とは?

Argparse は、使いやすいコマンドラインインターフェースを簡単に作成できる Python の組み込みモジュールです。ヘルプメッセージを自動的に生成し、エラーを処理し、コマンドライン引数をプログラムに適したデータ型に変換します。

Argparse を使用した最初のスクリプトの作成

argparse を使用する簡単な Python スクリプトを作成することから始めましょう。コマンドライン引数として名前を受け入れる hello.py というファイルを作成します。

  1. VSCode エディタを開き、「ファイル」>「新しいファイル」をクリックして新しいファイルを作成します。
  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 を使用するための 3 つの主要なステップを示しています。

  1. ArgumentParser オブジェクトの作成
  2. パーサーへの引数の追加
  3. コマンドライン引数の解析

スクリプトの実行

スクリプトを実行して、その動作を確認しましょう。

  1. VSCode インターフェースでターミナルを開きます(ターミナル > 新しいターミナル)。
  2. 次のコマンドでスクリプトを実行します。
python3 hello.py

次のような出力が表示されるはずです。

Hello, World!

次に、名前を渡してみましょう。

python3 hello.py --name Alice

出力:

Hello, Alice!

ヘルプメッセージの理解

argparse の利点の 1 つは、ヘルプメッセージを自動的に生成することです。次を実行してみてください。

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' 引数の値にアクセスします。

これで、Python でコマンドライン引数を処理するために argparse を使用する基本を理解できました。

複数の引数タイプの追加

基本を理解したところで、コマンドラインインターフェースに追加できるさまざまな引数タイプについて見ていきましょう。

引数の種類

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. 位置引数: operation - 'add', 'subtract', 'multiply', または 'divide' のいずれかでなければなりません。

    parser.add_argument('operation',
                        choices=['add', 'subtract', 'multiply', 'divide'],
                        help='Math operation to perform')
  2. required=True のオプション引数: --num1--num2 - ユーザーが提供する必要があります。

    parser.add_argument('--num1', type=float, required=True,
                        help='First number')
  3. フラグ引数: --verbose - 存在する場合、詳細出力を有効にします。

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

計算機のテスト

計算機をさまざまな引数で実行して、その動作を確認します。

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

出力:

Result: 8.0

詳細フラグを試してください。

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. カスタム検証 (validation) を持つ引数
  3. デフォルト値を持つ引数
  4. 短い名前 (1 文字のエイリアス) を持つ引数

ファイル処理スクリプトの作成

ファイル処理ユーティリティを構築することにより、これらの高度な設定を示すスクリプトを作成しましょう。

  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='+' パラメータを使用すると、ユーザーは 1 つ以上のファイル引数を指定できます。

  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)')

    これにより、モードが特定の値に制限され、デフォルトが提供されます。

サンプルテストファイルの作成

スクリプトをテストするために、2 つのサンプルファイルを作成しましょう。

  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
  1. 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. 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
  1. 複数のファイルを処理する:
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
  1. 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
  1. 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
  1. 行数を制限する:
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='+' は、複数の引数値を使用できます。
  • カスタム検証関数は、有効な入力を保証するのに役立ちます。
  • 短い引数名 (1 文字のエイリアス) は、利便性を提供します。
  • デフォルト値は、一般的なシナリオでの使用を簡素化します。
  • 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) - add、list、delete コマンドの異なる引数
  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
  1. タスクの一覧表示:
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
  1. フィルタリングとソートを使用したタスクの一覧表示:
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
  1. 優先度によるフィルタリング:
python3 task_manager.py list -p high

出力:

ID | Title                 | Priority | Due Date
--------------------------------------------------
1  | Complete Python tutor | high     | 2023-12-31
  1. タスクの削除:
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 プロジェクトに適用して、ユーザーにとってより多用途でアクセスしやすくすることができます。