Argparse 자습서¶
- 저자
Tshepang Lekhonkhobe
이 자습서는 파이썬 표준 라이브러리에서 권장하는 명령행 파싱 모듈인 argparse
에 대한 소개입니다.
참고
같은 작업을 수행하는 다른 두 모듈이 있습니다, getopt
(C 언어에서 getopt()
와 동등합니다) 와 폐지된 optparse
. argparse
는 optparse
에 기반을 두고 있어서 사용법 면에서 매우 비슷합니다.
개념¶
ls 명령을 사용하여 이 입문서에서 다룰 기능들을 살펴봅시다:
$ ls
cpython devguide prog.py pypy rm-unused-function.patch
$ ls pypy
ctypes_configure demo dotviewer include lib_pypy lib-python ...
$ ls -l
total 20
drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
drwxr-xr-x 4 wena wena 4096 Feb 8 12:04 devguide
-rwxr-xr-x 1 wena wena 535 Feb 19 00:05 prog.py
drwxr-xr-x 14 wena wena 4096 Feb 7 00:59 pypy
-rw-r--r-- 1 wena wena 741 Feb 18 01:01 rm-unused-function.patch
$ ls --help
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
...
네 가지 명령에서 배울 수 있는 몇 가지 개념들입니다:
ls 명령은 옵션 없이 실행될 때도 유용합니다. 기본적으로 현재 디렉터리의 내용을 표시합니다.
기본적으로 제공하는 것 이상으로 원한다면, 조금 더 말합니다. 이 경우에는 다른 디렉터리인
pypy
를 표시하기를 원합니다. 우리가 한 것은 위치 인자라고 알려진 것을 지정하는 것입니다. 프로그램이 명령행에 표시되는 위치를 기준으로 값을 어떻게 처리해야 하는지를 알아야 하므로 이런 이름이 사용됩니다. 이 개념은 cp 와 같은 명령에 더 적절합니다. 가장 기본적인 사용법은cp SRC DEST
입니다. 첫 번째 위치는 복사하고자 하는 것 이고 두 번째 위치는 사본을 저장할 곳 입니다.자, 프로그램의 행동을 바꾸고 싶다고 합시다. 이 예에서는 파일 이름만 표시하는 대신 각 파일에 대한 정보를 더 많이 표시합니다. 이 경우
-l
은 옵션 인자로 알려져 있습니다.이것이 도움말 텍스트입니다. 이전에 사용해보지 않은 프로그램을 접했을 때 도움말 텍스트를 읽는 것만으로 작동하는 방식을 이해할 수 있다는 점에서 매우 유용합니다.
기본¶
(거의) 아무것도 하지 않는 아주 간단한 예제로 시작합시다:
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
다음은 코드를 실행한 결과입니다:
$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h]
optional arguments:
-h, --help show this help message and exit
$ python3 prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python3 prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo
일어난 일은 이렇습니다:
옵션 없이 스크립트를 실행하면 아무것도 표준 출력에 표시되지 않습니다. 별로 유용하지 않습니다.
두 번째는
argparse
모듈의 쓸모를 보여주기 시작합니다. 거의 아무것도 하지 않았지만 이미 도움말을 얻었습니다.--help
옵션은,-h
로 단축할 수도 있습니다, 무료로 얻을 수 있는 유일한 옵션입니다 (즉, 지정할 필요가 없습니다). 다른 값을 지정하면 오류가 발생합니다. 그러나 그때조차도 우리는 사용 안내를 얻습니다, 여전히 공짜입니다.
위치 인자 소개¶
예:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)
코드를 실행합니다:
$ python3 prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python3 prog.py --help
usage: prog.py [-h] echo
positional arguments:
echo
optional arguments:
-h, --help show this help message and exit
$ python3 prog.py foo
foo
이런 일이 일어났습니다:
add_argument()
메서드를 추가했습니다. 이 메서드는 프로그램이 받고 싶은 명령행 옵션을 지정하기 위해 사용합니다. 이 경우 기능과 일치하도록echo
라고 이름 붙였습니다.이제 프로그램을 호출하려면 옵션을 지정해야 합니다.
parse_args()
메서드는 실제로 지정된 옵션으로부터 온 데이터를 돌려줍니다. 이 경우에는echo
입니다.변수는
argparse
이 공짜로 수행하는 일종의 〈마법〉 입니다 (즉, 값이 저장되는 변수를 지정할 필요가 없습니다). 또한, 그 이름이 메서드에 주어진 문자열 인자echo
와 일치함을 알 수 있습니다.
그러나 도움말이 멋지게 보이지만, 현재로서는 가능한 최선이 아닙니다. 예를 들어 echo
가 위치 인자임을 볼 수 있지만, 추측하거나 소스 코드를 읽는 것 외에는 그것이 무엇을 하는지 모릅니다. 그럼 좀 더 유용하게 만들어 봅시다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print(args.echo)
그러면 이렇게 됩니다:
$ python3 prog.py -h
usage: prog.py [-h] echo
positional arguments:
echo echo the string you use here
optional arguments:
-h, --help show this help message and exit
이제, 뭔가 더 쓸모있는 일을 하는 것은 어떻습니까:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)
다음은 코드를 실행한 결과입니다:
$ python3 prog.py 4
Traceback (most recent call last):
File "prog.py", line 5, in <module>
print(args.square**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
잘 안됐습니다. 우리가 달리 지시하지 않는다면, argparse
는 우리가 준 옵션들을 문자열로 취급하기 때문입니다. 그럼, argparse
에게 그 입력을 정수로 취급하라고 알려줍시다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number",
type=int)
args = parser.parse_args()
print(args.square**2)
다음은 코드를 실행한 결과입니다:
$ python3 prog.py 4
16
$ python3 prog.py four
usage: prog.py [-h] square
prog.py: error: argument square: invalid int value: 'four'
잘 됩니다. 이제 이 프로그램은 잘못된 입력이 올 때 더 진행하지 않고 종료하기조차 합니다.
옵션 인자 소개¶
지금까지 우리는 위치 인자를 다뤘습니다. 옵션 인자를 추가하는 방법에 대해 살펴봅시다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
print("verbosity turned on")
출력은 이렇습니다:
$ python3 prog.py --verbosity 1
verbosity turned on
$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]
optional arguments:
-h, --help show this help message and exit
--verbosity VERBOSITY
increase output verbosity
$ python3 prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument
일어난 일은 이렇습니다:
이 프로그램은
--verbosity
가 지정되었을 때 어떤 것을 표시하고 그렇지 않을 때는 아무것도 표시하지 않도록 작성되었습니다.옵션이 실제로 선택 사항임을 확인하기 위해, 이 옵션을 사용하지 않고 프로그램을 실행할 때 오류가 없습니다. 기본적으로 옵션 인자가 사용되지 않는다면 관련 변수 (이 경우
args.verbosity
)는 값으로None
이 주어집니다. 이 때문에if
문의 논리값 검사가 실패합니다.도움말 메시지가 약간 달라졌습니다.
--verbosity
옵션을 사용할 때, 어떤 값을 지정해야 합니다. 어떤 값이건 상관없습니다.
위의 예제는 --verbosity
에 임의의 정숫값을 허용하지만, 우리의 간단한 프로그램에서는 실제로 True
또는 False
두 값만 쓸모 있습니다. 그것에 맞게 코드를 수정합시다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
출력은 이렇습니다:
$ python3 prog.py --verbose
verbosity turned on
$ python3 prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python3 prog.py --help
usage: prog.py [-h] [--verbose]
optional arguments:
-h, --help show this help message and exit
--verbose increase output verbosity
일어난 일은 이렇습니다:
이 옵션은 이제 값을 요구하는 것이 아니라 플래그입니다. 이 개념과 일치하도록 옵션의 이름을 변경하기까지 했습니다. 새로운 키워드
action
을 지정하고,"store_true"
값을 지정했습니다. 이것은, 옵션이 지정되면args.verbose
에 값True
를 대입하라는 뜻입니다. 지정하지 않으면 묵시적으로False
입니다.값을 지정하면 불평하는데, 플래그의 정의를 따르고 있습니다.
도움말 텍스트가 바뀐 것을 확인하십시오.
짧은 옵션¶
명령행 사용법에 익숙하다면 짧은 옵션 버전에 관한 내용을 아직 다루지 않았음을 알 수 있을 겁니다. 아주 간단합니다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
그러면 이렇게 됩니다:
$ python3 prog.py -v
verbosity turned on
$ python3 prog.py --help
usage: prog.py [-h] [-v]
optional arguments:
-h, --help show this help message and exit
-v, --verbose increase output verbosity
새로운 기능은 도움말 텍스트에도 반영됩니다.
위치 및 옵션 인자 결합하기¶
프로그램이 점점 복잡해지고 있습니다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
print("the square of {} equals {}".format(args.square, answer))
else:
print(answer)
이제 출력은 이렇게 됩니다:
$ python3 prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python3 prog.py 4
16
$ python3 prog.py 4 --verbose
the square of 4 equals 16
$ python3 prog.py --verbose 4
the square of 4 equals 16
위치 인자를 다시 도입했기 때문에, 불평합니다.
순서는 중요하지 않습니다.
이 프로그램에 여러 상세도를 지정할 수 있도록 하는 능력을 다시 부여하고, 실제로 그것을 사용하는 것은 어떨까요:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
출력은 이렇습니다:
$ python3 prog.py 4
16
$ python3 prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python3 prog.py 4 -v 1
4^2 == 16
$ python3 prog.py 4 -v 2
the square of 4 equals 16
$ python3 prog.py 4 -v 3
16
우리 프로그램의 버그를 드러내는 마지막 것을 제외하고는 그럴듯해 보입니다. --verbosity
옵션이 받아들일 수 있는 값을 제한해서 고쳐봅시다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
출력은 이렇습니다:
$ python3 prog.py 4 -v 3
usage: prog.py [-h] [-v {0,1,2}] square
prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square
positional arguments:
square display a square of a given number
optional arguments:
-h, --help show this help message and exit
-v {0,1,2}, --verbosity {0,1,2}
increase output verbosity
변경 내용은 오류 메시지와 도움말 문자열에도 반영됩니다.
이제 상세도를 다루는 다른 접근법을 사용해 봅시다, 이 방법은 꽤 널리 사용됩니다. 또한, 이 방법은 CPython 실행 파일이 자신의 상세도를 처리하는 방식과도 일치합니다 (python --help
의 결과를 확인하십시오):
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display the square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
특정 옵션 인자를 지정한 횟수를 계산하기 위해 《count》 라는 또 다른 액션을 도입했습니다:
$ python3 prog.py 4
16
$ python3 prog.py 4 -v
4^2 == 16
$ python3 prog.py 4 -vv
the square of 4 equals 16
$ python3 prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python3 prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v] square
positional arguments:
square display a square of a given number
optional arguments:
-h, --help show this help message and exit
-v, --verbosity increase output verbosity
$ python3 prog.py 4 -vvv
16
예, 이제 이전 버전의 스크립트처럼 (
action="store_true"
와 유사하게) 플래그가 되었습니다. 출력되는 불평이 설명됩니다.또한 《store_true》 액션과 비슷하게 작동하기도 합니다.
이제 여기에서 《count》 액션이 제공하는 것을 보여줍니다. 이런 종류의 사용법을 전에도 보았을 것입니다.
그리고,
-v
플래그를 지정하지 않으면 그 플래그는``None`` 값으로 간주합니다.예측하듯이, 플래그의 긴 형식을 지정하면, 같은 출력이 얻어져야 합니다.
안타깝게도 스크립트가 얻은 새로운 기능에 대한 도움말 출력은 그다지 유익하지 않지만, 스크립트의 문서를 개선하면 항상 해결할 수 있습니다 (예,
help
키워드 인자를 사용해서).마지막 출력은 우리 프로그램의 버그를 노출합니다.
고칩시다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
# bugfix: replace == with >=
if args.verbosity >= 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity >= 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
그러면 이렇게 됩니다:
$ python3 prog.py 4 -vvv
the square of 4 equals 16
$ python3 prog.py 4 -vvvv
the square of 4 equals 16
$ python3 prog.py 4
Traceback (most recent call last):
File "prog.py", line 11, in <module>
if args.verbosity >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
첫 번째 출력은 잘 동작하고, 앞에서 나온 버그를 고칩니다. 즉, 2보다 크거나 같은 (>=) 모든 값을 최대의 상세도로 취급하고 싶습니다.
세 번째 결과가 좋지 않습니다.
이 버그를 고쳐 봅시다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", default=0,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity >= 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity >= 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
또 다른 키워드 default
를 소개했습니다. 다른 int 값과 비교하기 위해 0
으로 설정했습니다. 기본적으로, 옵션 인자가 지정되지 않으면 None
값을 갖게 되고, 그것은 int 값과 비교될 수 없음을 (그래서 TypeError
예외를 일으킵니다) 기억하십시오.
그리고:
$ python3 prog.py 4
16
여러분은 지금까지 배운 것만으로도 아주 멀리 갈 수 있으며, 우리는 단지 표면을 긁었을 뿐입니다. argparse
모듈은 매우 강력합니다. 이 자습서를 끝내기 전에 좀 더 탐색해 보겠습니다.
조금 더 발전시키기¶
우리의 작은 프로그램을 확장하여 제곱만이 아닌 다른 거듭제곱을 수행하기를 원하면 어떻게 될까요:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
print("{} to the power {} equals {}".format(args.x, args.y, answer))
elif args.verbosity >= 1:
print("{}^{} == {}".format(args.x, args.y, answer))
else:
print(answer)
출력:
$ python3 prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python3 prog.py -h
usage: prog.py [-h] [-v] x y
positional arguments:
x the base
y the exponent
optional arguments:
-h, --help show this help message and exit
-v, --verbosity
$ python3 prog.py 4 2 -v
4^2 == 16
지금까지는 표시되는 텍스트를 변경 하기 위해 상세도를 사용했습니다. 다음 예제는 대신 더 많은 텍스트를 표시하기 위해 상세도를 사용합니다:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
print("Running '{}'".format(__file__))
if args.verbosity >= 1:
print("{}^{} == ".format(args.x, args.y), end="")
print(answer)
출력:
$ python3 prog.py 4 2
16
$ python3 prog.py 4 2 -v
4^2 == 16
$ python3 prog.py 4 2 -vv
Running 'prog.py'
4^2 == 16
충돌하는 옵션들¶
지금까지 우리는 argparse.ArgumentParser
인스턴스의 두 가지 메서드로 작업 해왔습니다. 세 번째를 소개합시다, add_mutually_exclusive_group()
. 이것은 서로 배타적인 옵션을 지정할 수 있도록 합니다. 새로운 기능을 더 잘 이해할 수 있도록 프로그램의 나머지 부분을 변경해 보겠습니다: --quiet
옵션을 도입하는데 --verbose
의 반대입니다:
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y
if args.quiet:
print(answer)
elif args.verbose:
print("{} to the power {} equals {}".format(args.x, args.y, answer))
else:
print("{}^{} == {}".format(args.x, args.y, answer))
프로그램은 이제 더 간단 해졌으며, 데모를 위해 일부 기능을 잃어버렸습니다. 어쨌든, 출력은 이렇습니다:
$ python3 prog.py 4 2
4^2 == 16
$ python3 prog.py 4 2 -q
16
$ python3 prog.py 4 2 -v
4 to the power 2 equals 16
$ python3 prog.py 4 2 -vq
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
$ python3 prog.py 4 2 -v --quiet
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
따라가기 쉽습니다. 여러분이 얻는 유연성을 볼 수 있도록 마지막 출력을 추가했습니다. 즉, 긴 형식 옵션을 짧은 형식 옵션과 섞어 쓸 수 있습니다.
결론을 내리기 전에, 여러분은 아마도 사용자들이 모를 경우를 대비해서 프로그램의 주요 목적을 알려주기를 원할 것입니다:
import argparse
parser = argparse.ArgumentParser(description="calculate X to the power of Y")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y
if args.quiet:
print(answer)
elif args.verbose:
print("{} to the power {} equals {}".format(args.x, args.y, answer))
else:
print("{}^{} == {}".format(args.x, args.y, answer))
사용법 텍스트의 약간의 차이점에 유의하십시오. [-v | -q]
에 주목해야 하는데, -v
나 -q
를 사용할 수 있지만 동시에 둘 다를 사용할 수는 없다는 뜻입니다:
$ python3 prog.py --help
usage: prog.py [-h] [-v | -q] x y
calculate X to the power of Y
positional arguments:
x the base
y the exponent
optional arguments:
-h, --help show this help message and exit
-v, --verbose
-q, --quiet