본문 바로가기
언어별 개념 정리/Python

Python argparse에서 False가 True로 읽히는 이유

by char_lie 2026. 9. 15.
반응형

Python argparse에서 False 문자열과 불리언 옵션 처리 차이를 정리한 표지

명령행 옵션을 type=bool로 선언하면 문자열 "False"도 True로 변환된다. Python의 bool()은 글자의 뜻을 해석하는 함수가 아니라 값이 비어 있는지 등을 기준으로 참·거짓을 판단하기 때문이다. 켜고 끄는 옵션은 store_true나 BooleanOptionalAction으로 표현하고, --enabled false처럼 값을 받는 형식이 꼭 필요할 때만 변환 함수를 만든다.

아래 Python 코드 블록은 각각 독립 실행 예제다. 외부 패키지는 필요 없으며, BooleanOptionalAction을 사용하는 예제는 Python 3.9 이상에서 실행한다. parse_args([...])에 명령행 인수를 직접 넣어 결과를 재현한다.

type=bool이 실제로 하는 일

첫 예제는 잘못된 선택이 어떤 결과를 만드는지 보여준다. 문법 오류가 발생하는 코드는 아니다.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--enabled", type=bool)

for value in ["True", "False", "0", ""]:
    args = parser.parse_args(["--enabled", value])
    print(repr(value), args.enabled)

출력은 다음과 같다.

'True' True
'False' True
'0' True
'' False

문자열 "False"와 "0"는 모두 비어 있지 않다. 따라서 bool("False")도 bool("0")도 True다. 셸에서 값을 따옴표로 감싸는 것만으로 해결되지 않는다. 따옴표는 문자열을 전달하는 방법을 바꿀 뿐, bool()의 판단 규칙을 바꾸지는 않는다.

옵션이 있으면 켜기: store_true

기본값이 꺼짐이고 옵션을 적었을 때 켜는 기능이라면 다음과 같이 쓴다.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--verbose", action="store_true")

for argv in [[], ["--verbose"]]:
    print(argv, parser.parse_args(argv).verbose)

결과는 [] False, ['--verbose'] True다. 실제 스크립트에서는 parser.parse_args()처럼 인수를 생략하면 터미널에서 받은 인수를 읽는다.

이 옵션은 뒤에 값을 받지 않는다. --verbose false라고 입력하면 false는 별도의 인수가 된다. 다른 위치 인수를 선언하지 않은 이 예제에서는 처리할 수 없는 인수 오류가 발생한다. 기본값이 켜짐이고 옵션을 적었을 때 끄려면 action="store_false"가 대응되는 선택이다.

켜기와 끄기를 함께 제공하기

--cache와 --no-cache를 모두 제공하려면 BooleanOptionalAction을 사용할 수 있다.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument(
    "--cache",
    action=argparse.BooleanOptionalAction,
    default=None,
)

for argv in [[], ["--cache"], ["--no-cache"]]:
    print(argv, parser.parse_args(argv).cache)

출력은 다음과 같다.

[] None
['--cache'] True
['--no-cache'] False

여기서는 옵션을 생략한 상태와 명시적으로 끈 상태를 구분하려고 기본값을 None으로 정했다. 설정 파일의 캐시 값을 우선 쓰되 명령행에서 지정한 경우만 덮어쓰고 싶을 때 유용하다. 항상 기본값을 꺼짐으로 만들 목적이라면 default=False로 바꾸면 된다.

둘을 함께 전달하면 기본적으로 뒤에 나온 값이 반영된다. 함께 쓰는 것을 오류로 막아야 하는 도구라면 별도 상호 배타 그룹과 두 옵션을 구성해야 한다. 도움말과 실제 허용 문법을 같은 기준으로 정하는 것이 좋다.

true와 false 값을 직접 받아야 한다면

외부 실행 도구가 --enabled false 형식만 만들 수 있다면 허용할 문자열을 직접 정의한다. 다음 예제는 대소문자를 구분하지 않고 true와 false 두 값만 받는다. 알 수 없는 값은 조용히 꺼짐으로 바꾸지 않고 오류로 처리한다.

import argparse

def parse_bool(value):
    normalized = value.casefold()
    if normalized == "true":
        return True
    if normalized == "false":
        return False
    raise argparse.ArgumentTypeError("true 또는 false를 입력하세요")

parser = argparse.ArgumentParser()
parser.add_argument("--enabled", type=parse_bool, required=True)

for argv in [["--enabled", "false"], ["--enabled", "TRUE"]]:
    print(argv, parser.parse_args(argv).enabled)

출력은 False, True로 끝난다. 실제 파일로 만들 때는 마지막 반복문 대신 args = parser.parse_args()를 쓰면 된다. --enabled maybe처럼 허용하지 않은 값을 넣으면 argparse가 사용법과 변환 오류를 표시하고 종료 코드 2로 끝낸다. required=True이므로 옵션 자체를 빠뜨린 경우도 오류다.

이 함수는 공백을 자동 제거하지 않고 1·0·yes·no도 받지 않는다. 입력 계약을 넓혀야 할 때만 허용값을 추가한다. 어떤 문자열을 받든 bool(value)로 바꾸는 방식은 다시 같은 문제를 만든다.

값 없는 단방향 플래그는 store_true, 켜기·끄기 쌍은 BooleanOptionalAction, 문자열 값이 필수인 인터페이스는 검증 함수를 선택하면 된다. 구현 후에는 옵션 생략·켜기·끄기·잘못된 값까지 실행해 출력과 종료 코드를 확인한다.

반응형

댓글