読者です 読者をやめる 読者になる 読者になる

Pythonのisdigit(),isdecimal(),isnumeric()の違いを調べてみた

いつもtwitterでお世話になってる方のブログ記事でこんな記述がありました。
#91 [Python][TDD]テスト駆動開発でFizzBuzzしてみようず! « Python « Gab_kmのブログ

str型には、次の検証用メソッドがあります。

str.isdecimal()
str.isdigit()
str.isnumeric()

正直、私にはこれらの違いが分かりませんでした(´・ω・`)

Python2.7には isdigit() しかないようなのでPython3.2あたりで追加されたんでしょうか。気になったので違いを検証してみました。

検証

num = "1" # まずはunicode
num.isdigit()   # True
num.isdecimal() # True
num.isnumeric() # True

num = "" # 全角で渡してみる。
num.isdigit()   # True
num.isdecimal() # True
num.isnumeric() # True

num = b"1" # byteで渡してみる
num.isdigit()   # True
num.isdecimal() # AttributeError 'bytes' object has no attribute 'isdecimal'
num.isnumeric() # AttributeError 'bytes' object has no attribute 'isnumeric'

num = "IV" # ローマ数字で渡してみる
num.isdigit()   # True
num.isdecimal() # False
num.isnumeric() # True

num = "" # 漢数字で渡してみる
num.isdigit()   # False
num.isdecimal() # False
num.isnumeric() # True

まとめるとこんな感じ。

isdigit()
True: ユニコード文字・バイト文字・全角文字・ローマ数字
False: 漢数字
Error: なし

isdecimal()
True: ユニコード文字・全角文字
False: ローマ数字・漢数字
Error: バイト文字

isnumeric()
True: ユニコード文字・全角・ローマ数字・漢数字
False: なし
Error: バイト文字

isdigit() は主にアルファベット圏の人が数字だと思うもので、isnumeric() は漢字圏の人の為に漢数字などの英語圏では割とマイナーな数字もサポートしましたよってもの、isdecimal() はアラビア数字しか認めませんよってものという解釈に落ち着きました。

漢数字やペルシア文字などがどの程度通るか実験。

※「百」の左は「卌」です。はてダのスーパーpre記法中では文字化けしてしまうようです。

# coding:utf-8
kanji = [
"","","","","","","","",
"","","","","","","","廿",
"","","","","","",""
]

for i in kanji:
    print('"{0}".isnumeric()-> {1}'.format(i, i.isnumeric()))

#"〇".isnumeric()-> True
#"零".isnumeric()-> True
#"一".isnumeric()-> True
#"壱".isnumeric()-> True
#"二".isnumeric()-> True
#"弐".isnumeric()-> True
#"三".isnumeric()-> True
#"参".isnumeric()-> True
#"四".isnumeric()-> True
#"五".isnumeric()-> True
#"六".isnumeric()-> True
#"七".isnumeric()-> True
#"八".isnumeric()-> True
#"九".isnumeric()-> True
#"十".isnumeric()-> True
#"廿".isnumeric()-> True
#"卅".isnumeric()-> True
#"卌".isnumeric()-> True
#"百".isnumeric()-> True
#"千".isnumeric()-> True
#"万".isnumeric()-> True
#"萬".isnumeric()-> True
#"億".isnumeric()-> True

Windowsの場合コマンドラインで実行するとUnicodeEncodeError吐きますが、IDLEとかだとこのような結果になります。はてダがペルシア文字等を「૫」などのように変換してしまうため例には含めませんでしたが、それらもちゃんとTrueが返りました。Unicodeで定義されてる数字ならほぼ通るんじゃないでしょうか。少なくとも日本人が普段使う漢数字は問題なく通ると思います。

おまけ:unicodedata モジュール

unicodedata モジュールには文字列が数字か判定するのではなくて、文字列を数値にして返すメソッドがあります。(これはPython 2.6の時点で存在する)

7.9. unicodedata — Unicode データベース — Python 2.7ja1 documentation

unicodedata.digit("2")   # 2
unicodedata.decimal("2") # 2
unicodedata.numeric("2") # 2.0

unicodedata.digit("")   # 2
unicodedata.decimal("") # 2
unicodedata.numeric("") # 2.0

unicodedata.digit(b"3")   # TypeError: must be str, not bytes
unicodedata.decimal(b"3") # TypeError: must be str, not bytes
unicodedata.numeric(b"3") # TypeError: must be str, not bytes

unicodedata.digit("VIII")   # ValueError: not a digit
unicodedata.decimal("VIII") # ValueError: not a decimal
unicodedata.numeric("VIII") # 8.0

unicodedata.digit("")   # ValueError: not a digit
unicodedata.decimal("") # ValueError: not a decimal
unicodedata.numeric("") # 4.0

unicode文字を検査するメソッドなのでバイト文字列を渡したらエラーになるのは当たり前として、ここでもnumericは何でも数値として認識してくれるようなので、ユーザーが漢数字で入力してもちゃんと計算してくれるシステムとかが簡単に実装できそうです。まあどの程度が環境依存なのかがちょっと怖いですが。