Pythonのargparseでブール値を扱うときは注意が必要

Pythonでコマンドライン引数を扱うには、sysモジュールのargvかargparseモジュールを使います。

argparseモジュールを使うとコマンドライン引数を柔軟に処理できるが、ブール値(True, False)を扱う場合は注意が必要。 以下の内容について説明します。

引数を簡単に定義できるargparse argparseで引数の型(type)を指定 add_argument()の引数typeにboolを指定してはいけない 組み込み関数bool()による判定 引数typeではなく引数actionを使う 関数strtobool()を使う

引数を簡単に定義できるargparse

argparseモジュールを使うと、コマンドラインの引数を簡単に定義できます。

argparse モジュールはユーザーフレンドリなコマンドラインインターフェースの作成を簡単にします。プログラムがどんな引数を必要としているのかを定義すると、argparse が sys.argv からそのオプションを解析する方法を見つけ出します。argparse モジュールは自動的にヘルプと使用方法メッセージを生成し、ユーザーが不正な引数をプログラムに指定したときにエラーを発生させます。 16.4. argparse — コマンドラインオプション、引数、サブコマンドのパーサー — Python 3.6 ドキュメント

argparseで引数の型(type)を指定

argparseで便利なのが、型(type)の指定します。 例えば、整数(int)型を指定しておくと、引数を自動でintに変換してくれて、さらにintではない引数に対してエラーが発生するようになります。 add_argument()の引数typeで型を指定します。

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('arg_int', type=int)

args = parser.parse_args()
print(args.arg_int)
print(type(args.arg_int))

このファイルをコマンドラインから実行します。

$ python argparse_type_int.py 100
100
<type 'int'>

引数100がintとして読み込まれています。 intではない値を引数とすると、エラーが発生します。

$ python argparse_type_int.py foo
usage: argparse_type_int.py [-h] arg_int
argparse_type_int.py: error: argument arg_int: invalid int value: 'foo'

$ python argparse_type_int.py 1.23
usage: argparse_type_int.py [-h] arg_int
argparse_type_int.py: error: argument arg_int: invalid int value: '1.23'

想定外の引数を弾けるのでとても便利です。

add_argument()の引数typeにboolを指定してはいけない

注意が必要なのがbool。intやfloatなどのように、add_argument()の引数typeにboolを指定すると、想定通りの動作をしてくれない。

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('arg_bool', type=bool)

args = parser.parse_args()
print(args.arg_bool)
print(type(args.arg_bool))

このファイルをコマンドラインから実行します。 $ python argparse_type_bool.py True True <type 'bool'>

Trueを引数とすると、bool型のTrueとして読み込まれる。これは想定通りの動作だが、問題は次の場合です。 $ python argparse_type_bool.py False True <type 'bool'>

$ python argparse_type_bool.py bar True <type 'bool'>

Falseや他の文字列を引数にしてもTrueとして読み込まれてしまいます。 なぜこのようなことになってしまうかというと、add_argument()でtype=xxxと指定すると、引数が関数xxx()に渡されるから。 例えば、type=intとすると、引数が、文字列を整数に変換する組み込み関数int()に渡されます。type=floatの場合はfloat()。

  1. 組み込み関数 — Python 3.6.1 ドキュメント int()

type=boolの場合も同じで、引数が関数bool()に渡されることになります。

組み込み関数bool()による判定

このbool()が曲者。

  1. 組み込み関数 — Python 3.6.1 ドキュメント bool()
  2. 組み込み型 — Python 3.6.1 ドキュメント 真理値判定 以下の値は偽と見なされます:

None False 数値型におけるゼロ。例えば 0, 0.0, 0j 。 空のシーケンス。例えば '', (), [] 。 空のマッピング。例えば {} 。

それ以外の全ての値は真と見なされます — 従って、多くの型のオブジェクトは常に真です。ブール値の結果を返す演算および組み込み関数は、特に注釈のない限り常に偽値として 0 または False を返し、真値として 1 または True を返します。

したがって、'True'だろうが'False'だろうが、空ではない文字列をbool()に渡すと、すべてTrueが返ってくる。空文字列のみがFalseとなります。

print(bool('True'))
print(bool('False'))
print(bool('abc'))
# True
# True
# True

print(bool(''))
# False

add_argument()でtype=boolとすると引数がbool()に渡されるため、上の例のように、Falseを引数とした場合は文字列'False'としてbool()で変換され、Trueとして読み込まれてしまいます。 bool型についての詳細は

引数typeではなく引数actionを使う

argparseでブール値を使いたい場合は、typeではなく引数actionに'store_true'または'store_false'を指定します。

'store_true', 'store_false' - これらは 'store_const' の、それぞれ True と False を格納する特別版になります。加えて、これらはそれぞれデフォルト値を順に False と True にします。 16.4. argparse — コマンドラインオプション、引数、サブコマンドのパーサー — Python 3.6 ドキュメント

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--en', action='store_true')

args = parser.parse_args()
print(args.en)
print(type(args.en))

この例の場合、オプション--enをつけるとenがTrueとして、つけないとenがデフォルト値のFalseとして読み込まれる。 $ python argparse_option_bool.py --en True <type 'bool'>

$ python argparse_option_bool.py False <type 'bool'>

デフォルトをTrueにして、オプションを付けたときにFalseとしたい場合は、action='store_false'とすれば問題ありません。

関数strtobool()を使う

オプションではなく位置引数を使いたい場合は、関数strtobool()を使う方法もあります。 strtobool()は文字列を真(1)または偽(0)に変換する関数。

真偽値をあらわす文字列を真(1)または偽(0)に変換します。真の値は y, yes, t, true, on そして 1 です。偽の値は n, no, f, false, off そして 0 です。 val が上のどれでもない時は ValueError を起こします。 10. API リファレンス — Python 3.6.1 ドキュメント strtobol()

文字列'y', 'yes', 'true', 'on', '1'は真(1)、文字列'n', 'no', 'f', 'false', 'off', '0'は偽(0)を返します。大文字小文字は関係ないので'TRUE'や'True, 'YES'などでも問題ありません。それ以外の文字列はエラーになります。

from distutils.util import strtobool

print(strtobool('true'))
print(strtobool('True'))
print(strtobool('TRUE'))
# 1
# 1
# 1

print(strtobool('t'))
print(strtobool('yes'))
print(strtobool('y'))
print(strtobool('on'))
print(strtobool('1'))
# 1
# 1
# 1
# 1
# 1

print(strtobool('false'))
print(strtobool('False'))
print(strtobool('FALSE'))
# 0
# 0
# 0

print(strtobool('f'))
print(strtobool('no'))
print(strtobool('n'))
print(strtobool('off'))
print(strtobool('0'))
# 0
# 0
# 0
# 0
# 0

# print(strtobool('abc'))
# ValueError: invalid truth value 'abc'

strtobool()という名前だが、返り値はbool型ではなくint型(1または0)。

print(type(strtobool('true')))
# <class 'int'>

先に書いたように、argparseのadd_argument()でtype=xxxと指定すると引数が関数xxx()に渡されるようになるので、type=strtoboolとすればOKです。

import argparse
from distutils.util import strtobool

parser = argparse.ArgumentParser()
parser.add_argument('arg_bool', type=strtobool)

args = parser.parse_args()
print(args.arg_bool)
print(type(args.arg_bool))

boolではなく、intの1か0ではあるが、trueやfalseを引数として真偽の値を読み込むことができます。 $ python argparse_type_strtobool.py true 1 <type 'int'>

$ python argparse_type_strtobool.py false 0 <type 'int'>

また、想定外の引数の場合はちゃんとエラーが発生します。 $ python argparse_type_strtobool.py bar usage: argparse_type_strtobool.py [-h] arg_bool argparse_type_strtobool.py: error: argument arg_bool: invalid strtobool value: 'bar' 関連カテゴリー Python

Pythonで経過時間や日時(日付・時刻)の差分を測定・算出 Python, Pillowで画像を回転するrotate pandas.DataFrameの各列間の相関係数を算出、ヒートマップで可視化 pandasの時系列データにおける頻度(引数freq)の指定方法 Pythonのリストと配列とnumpy.ndarrayの違いと使い分け Pythonで辞書のリストを特定のキーの値に従ってソート Pythonのdocstring(ドキュメンテーション文字列)の書き方 Python, Pillowで円や四角、直線などの図形を描画 Pythonで文字列・数値を右寄せ、中央寄せ、左寄せ Pythonでファイル・ディレクトリを移動するshutil.move Python, PyPDF2でPDFの作成者やタイトルなどを取得・削除・変更 pandas.DataFrameをJSON文字列・ファイルに変換・保存(to_json) Pythonで複数の比較演算子を連結して記述(a < x < bなど) Pythonで浮動小数点数floatと16進数表現の文字列を相互に変換 pandasで中央値を取得するmedian

Last Updated: 6/26/2019, 10:34:03 PM