isdigit()について

先日Pythonisnan()はそもそも文字列を評価できないので文字列が数値に変換可能な形("10"とか"0.5"など)かどうかを判定できないということを書いたのですが、Pythonにはisdigit()という文字列が数字かどうかを判定する標準の関数がありました。

print "0".isdigit() #True
print "abc".isdigit() #False
print "a3".isdigit() #False
print "500".isdigit() #True

ただし、isdigit()は文字列が数値だけかどうかを判定するので"."を含むfloat型はFalseになってしまいます。

print "0.0".isdigit() #False
print "3.14".isdigit() #False

小数点を含む場合もTrueと判定できるようにする方法を考えたのですが、最も単純な方法は"."をreplaceしてしまう方法でしょうか。

print "0.0".replace(".","").isdigit() #True
print "3.14".replace(".","").isdigit() #True

この方法は評価する文字列が"."を必ず一つしか含まないことが保証されている状況であれば正しく評価可能になりますがユーザーの入力を受け取る場合など、どのような文字列が入力されるか分からない状況では誤認の可能性もあります。

print "0..0".replace(".","").isdigit() #True
print "3.1.4".replace(".","").isdigit() #True
print "..5".replace(".","").isdigit() #True

そこで最初に現れる"."のみを置換する方法にしてみます。

print "0..0".replace(".","",1).isdigit() #False
print "3.1.4".replace(".","",1).isdigit() #False
print "..5".replace(".","",1).isdigit() #False
print "3.14".replace(".","",1).isdigit() #True
print "abc".replace(".","",1).isdigit() #False
print "5".replace(".","",1).isdigit() #True

これで"."が2つ以上登場するような値に対してはFalseを返すようになりましたが、まだ先頭に1つだけ"."がついているような値にはTrueを返してしまいます。

print ".14".replace(".","",1).isdigit() #True

このような場合にもFalseを返すようにするには先頭が数字かどうかをチェックすればいいですね。ただそうなると条件が2つになるのでここまでのように1行のパターンで書くのは難しいです。
これ以上1行でやるには正規表現を使わないと無理そうです。
こちらのページで紹介されている方法を改良して正規表現を使って判定する方法を書いてみました。

import re

print bool(re.compile("^\d+\.?\d*\Z").match("0")) #True
print bool(re.compile("^\d+\.?\d*\Z").match("5")) #True
print bool(re.compile("^\d+\.?\d*\Z").match("3.14")) #True
print bool(re.compile("^\d+\.?\d*\Z").match("1250.5833")) #True
print bool(re.compile("^\d+\.?\d*\Z").match("3..1")) #False
print bool(re.compile("^\d+\.?\d*\Z").match("3.1.4")) #False
print bool(re.compile("^\d+\.?\d*\Z").match(".5")) #False
print bool(re.compile("^\d+\.?\d*\Z").match("..5")) #False
print bool(re.compile("^\d+\.?\d*\Z").match("abc")) #False
print bool(re.compile("^\d+\.?\d*\Z").match("15a")) #False
print bool(re.compile("^\d+\.?\d*\Z").match("a15")) #False
print bool(re.compile("^\d+\.?\d*\Z").match(u"こんにちは")) #False

これで数値に変換可能かどうか判定できそうです。
(2/18追記。上の正規表現の書き方には補足があります→昨日のエントリの補足+α - 主にプログラムを勉強するブログ)