Python 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' 인수의 값에 액세스합니다.

이제 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. 위치 인수 (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

자세한 플래그로 시도해 보세요.

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
  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. 하나의 파일로 기본 사용법:
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='+'는 여러 인수 값을 허용합니다.
  • 사용자 지정 유효성 검사 함수는 유효한 입력을 보장하는 데 도움이 됩니다.
  • 짧은 인수 이름 (한 글자 별칭) 은 편의성을 제공합니다.
  • 기본값은 일반적인 시나리오에 대한 사용을 단순화합니다.
  • 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 프로젝트에 적용하여 사용자가 더 다양하고 쉽게 접근할 수 있도록 만들 수 있습니다.