모듈과 명령행 프로그램

Python 모듈, import와 argparse를 이용한 CLI 기초

1. 학습 목표

이번 강의가 끝나면 다음을 할 수 있어야 한다.

2. 모듈(module)이란

모듈(module)은 Python 코드를 저장한 .py 파일이다. 함수와 클래스를 모듈에 저장하면 다른 프로그램에서 다시 사용할 수 있다.

모듈로 나누면 긴 프로그램을 기능별로 정리하고 같은 코드를 여러 프로그램에서 사용할 수 있다.

3. 간단한 모듈 만들기

같은 폴더에 다음 두 파일을 만든다.

3.1. calculator.py

def add(a, b):
    return a + b


def multiply(a, b):
    return a * b


def power(base, exponent):
    return base**exponent

3.2. main.py

import calculator


print(calculator.add(3, 4))
print(calculator.multiply(3, 4))
print(calculator.power(3, 4))

터미널에서 main.py가 있는 폴더로 이동한 뒤 실행한다.

python main.py

예상 출력은 7, 12, 81이다.

모듈 이름과 호출 이름이 모두 calculator로 같은지 확인한다. 파일 이름이 calculator.py이면 import calculator라고 써야 한다.

4. import 방법

같은 모듈을 여러 방식으로 불러올 수 있다.

import math

print(math.sqrt(9))
from math import sqrt

print(sqrt(9))
import math as m

print(m.sqrt(9))

처음에는 import math처럼 모듈 이름을 남기는 방식이 함수의 출처를 알아보기 쉽다. from module import *는 이름 충돌이 생길 수 있으므로 사용하지 않는 편이 좋다.

5. 모듈을 찾는 위치

직접 만든 모듈을 불러오지 못한다면 다음을 확인한다.

  1. 파일 이름이 정확히 calculator.py인가?
  2. main.pycalculator.py가 같은 폴더에 있는가?
  3. 터미널의 현재 폴더가 두 파일이 있는 위치인가?
  4. 다른 파일이나 폴더가 같은 이름을 사용하고 있지 않은가?

현재 폴더는 터미널에서 다음 명령으로 확인할 수 있다.

pwd # pring working directory

Windows 명령 프롬프트에서는 다음 명령을 사용할 수 있다.

REM current directory
cd

6. 메인 프로그램과 __name__

다음 코드를 example.py에 저장하자.

def greet(name):
    return f"Hello, {name}!"


if __name__ == "__main__":
    print(greet("student"))

python example.py로 직접 실행하면 조건문 안의 코드가 실행된다. 다른 파일에서 import example로 불러오면 조건문 안은 실행되지 않는다.

따라서 함수와 클래스를 정의하는 부분과 프로그램을 직접 실행하는 부분을 분리할 수 있다.

7. 명령행 인자

명령행 인자(command-line argument)는 프로그램을 실행할 때 함께 전달하는 값이다.

다음 코드를 show_args.py에 저장한다.

import sys


if __name__ == "__main__":
    print(sys.argv)

다음과 같이 실행해 보자.

python show_args.py aluminum 27

sys.argv의 첫 번째 원소는 파일 이름이고 그 뒤에 입력값들이 문자열로 저장된다. 간단히 확인할 때는 유용하지만 입력값의 의미와 자료형을 관리하려면 argparse가 더 편리하다.

8. argparse 기초

8.1. 위치 인자 하나 받기

다음 코드를 square.py에 저장한다.

import argparse


def main():
    parser = argparse.ArgumentParser(
        description="Calculate the square of an integer."
    )
    parser.add_argument(
        "x",
        type=int,
        help="integer to be squared",
    )
    args = parser.parse_args()

    print(args.x**2)


if __name__ == "__main__":
    main()

도움말과 계산은 각각 다음처럼 실행한다.

python square.py --help
python square.py 3

두 번째 명령은 9를 출력한다. type=int를 지정했기 때문에 입력 문자열을 정수로 변환한다.

8.2. 선택 인자 추가하기

import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("x", type=float)
    parser.add_argument("y", type=float)
    parser.add_argument(
        "--operation",
        choices=["add", "multiply"],
        default="add",
    )
    args = parser.parse_args()

    if args.operation == "add":
        result = args.x + args.y
    else:
        result = args.x * args.y

    print(result)


if __name__ == "__main__":
    main()
python calculate.py 3 4
python calculate.py 3 4 --operation multiply

첫 명령은 기본 연산인 덧셈 결과 7을, 두 번째 명령은 곱셈 결과 12를 출력한다.

9. 재료공학 CLI 예제

질량과 부피를 입력받아 밀도를 계산하는 프로그램을 만들어 보자. 다음 코드를 density_cli.py에 저장한다.

import argparse

def density(mass_g, volume_cm3):
    if volume_cm3 <= 0:
        raise ValueError("volume must be positive")

    return mass_g / volume_cm3

def main():
    parser = argparse.ArgumentParser(
        description="Calculate density."
    )
    parser.add_argument(
        "mass",
        type=float,
        help="mass in g",
    )
    parser.add_argument(
        "volume",
        type=float,
        help="volume in cm^3",
    )
    args = parser.parse_args()

    rho = density(args.mass, args.volume)
    print(f"density = {rho:.3f} g/cm^3")


if __name__ == "__main__":
    main()

다음 명령을 실행하면 밀도 2.700 g/cm³가 출력된다.

python density_cli.py 27 10

함수 density는 계산만 담당하고 main은 입력 해석과 출력을 담당한다. 이렇게 역할을 분리하면 계산 함수를 다른 모듈에서도 재사용하기 쉽다.

10. 오류를 만났을 때

오류 메시지의 마지막 줄부터 읽으면 오류 종류와 원인을 찾기 쉽다.

11. 연습 문제

문제 1

Python 모듈 파일의 확장자를 쓰시오.

문제 2

tools.py라는 모듈을 불러오는 문장을 쓰시오.

문제 3

calculator 모듈의 add 함수에 2와 5를 전달하는 표현을 쓰시오.

문제 4

명령행 프로그램의 사용법을 확인할 때 사용하는 기본 선택 사항은 무엇인가?

문제 5

argparse에서 입력값을 정수로 변환하도록 지정하는 표현은 무엇인가?

문제 6

다음 명령에서 명령행 인자 두 개를 쓰시오.

python density_cli.py 27 10

12. 프로젝트형 연습문제: 이진수 변환 모듈과 CLI 만들기

5장 함수 기초에서 작성한 decimal_to_binary(number) 함수를 별도 모듈로 옮기고, 명령행에서 호출하는 프로그램을 작성하시오. 5장에서는 함수의 재사용을, 이번에는 파일 사이의 재사용과 명령행 입력 처리를 연습한다.

12.1. 두 파일의 역할

같은 폴더에 다음 두 파일을 만든다.

파일 역할
binary_tools.py decimal_to_binary(number) 함수 정의. 계산만 담당
binary_cli.py 모듈을 가져오고, 명령행 입력을 해석해 결과 출력

함수는 양의 정수의 2진수 문자열을 반환하고, 0이면 '0', 음수이면 None을 반환한다. 5장 프로젝트와 동일하게 while, //, %로 변환하며 bin()은 사용하지 않는다.

12.2. 구현 요구사항

  1. binary_tools.py에 변환 함수를 정의한다. 이 파일을 import할 때 출력이나 입력 요청이 발생하지 않아야 한다.
  2. binary_cli.py에서 import binary_tools로 모듈을 가져오고, binary_tools.decimal_to_binary(...)로 함수를 호출한다.
  3. argparse로 위치 인자 number를 받고 type=int를 지정한다.
  4. 음수이면 반환값 None을 확인하고 parser.error('음수는 지원하지 않습니다.')로 오류를 알린다.
  5. 정상 입력이면 입력값과 2진수 문자열을 함께 출력한다.
  6. CLI의 실행 코드는 main() 함수에 넣고, 메인 가드 아래에서 호출한다.
if __name__ == '__main__':
    main()

parser.error는 안내를 출력한 뒤 프로그램을 오류 상태로 종료한다. 정수가 아닌 입력이나 필수 인자 누락은 argparse가 처리한다.

12.3. 실행과 확인 사례

터미널에서 두 파일이 있는 폴더로 이동한 뒤 실행한다.

python binary_cli.py 13

기대 출력은 13 -> 1101이다.

명령 기대하는 동작
python binary_cli.py 0 0 -> 0 출력
python binary_cli.py 32 32 -> 100000 출력
python binary_cli.py -3 음수를 지원하지 않는다는 오류 안내
python binary_cli.py abc 정수 입력이 필요하다는 오류 안내
python binary_cli.py 필수 인자가 없다는 오류 안내
python binary_cli.py --help 사용법과 인자 설명 출력

Python에서 import binary_toolsimport binary_cli를 실행했을 때 아무것도 출력되지 않는지도 확인한다.

제출물: 두 Python 파일, 확인 사례별 실행 결과, 그리고 ‘계산 함수와 CLI를 분리한 이유, import와 메인 가드의 역할’에 대한 짧은 설명.