[Python]コマンドライン引数でboolを渡すときの注意点

コマンドライン引数を使う簡単なスクリプトを書くとき、引数としてTrue/Falseを渡して処理を分岐したいなんてことがあったのですが、見事にハマりました。

TL;DR

  • Pythonでは、空文字(”)以外の文字列はすべてTrue判定される
  • コマンドライン引数は、基本的にstr型で渡されるので、int()でキャストする必要がある

サンプルコード

引数で0 or 1を渡し、False or Trueとして処理を切り替えます。

前提として、Pythonにおいてはintとboolは以下です。

0 : False
1 : True (※正確には0以外のすべて)
# command_line_argv_sample.py
import sys

def main(argv):
    flg = bool(argv[1])
    if flg:
        print("Do something.")
    else:
        print("Don't do anything.")

if __name__ == '__main__':
    main(sys.argv)

実行してみると…

$ python command_line_argv_sample.py 0
Do something.

こうなります。

理由

コマンドライン引数では、値はすべてstr型で渡されるためです。

確認してみます。

...
def main(argv):
    print(type(argv[1]))
    flg = bool(argv[1])
    ...
$ python command_line_argv_sample.py 0
<class 'str'>
Do something.

対応

intでキャストする

flg = bool(int(argv[1]))
$ python command_line_argv_sample.py 0
Don't do anything.

うまくいきました。

intでキャストする場合の補足

intはそのままTrue/False判定ができるので、実はbool()でのキャストも不要です。

import sys

def main(argv):
    flg = int(argv[1])
    if flg:
        print("Do something.")
    else:
        print("Don't do anything.")

if __name__ == '__main__':
    main(sys.argv)

strtoboolを使う

True/Falseの文字列をTrue/Falseに変換する標準ライブラリがありました。

import sys
from distutils.util import strtobool

def main(argv):
    flg = strtobool(argv[1])
    if flg:
        print("Do something.")
    else:
        print("Don't do anything.")

if __name__ == '__main__':
    main(sys.argv)
$ python command_line_argv_sample.py False
Don't do anything.
$ python command_line_argv_sample.py True 
Do something.

こちらの方がコマンド実行する人に易しいですね。

参考

コマンドライン引数

真偽値