文字列のメソッド
isdigit()
2.3.6.1 文字列メソッド によると、
isdigit()
文字列中に数字しかない場合には真を返し、その他の場合は偽を返します。
気をつけることは、このメソッドが文字列のメソッドであるということ。
結果は、tests = ["100", "100.1", "", "hoge", "10hoge", "hoge10", "ho10ge", 100] for i,e in enumerate(tests): try: print e.isdigit() except Exception, e: print e, ':', "[%d]" % i, tests[i]
True False False False False False False 'int' object has no attribute 'isdigit' : [7] 100
数値 (int 型) は、文字列ではないので、例外が投げられる。
失敗談
フォームの内容を受けとるオブジェクトがあり、int 型を保持すると想定したインスタンス変数があった。フォームから値を受け取ったとき、その内容を検証する前に一時的に文字列としてそのインスタンス変数に設定し、後でフォーマットを検証し、妥当であれば int 型に変換し、再代入するということをしていた。この辺り、 Java のような強い型付けの言語ではコンパイルエラーとなってしまうが、動的な型付けのスクリプト系の言語は変数の使い回しができ、融通がきいて便利だなぁと思っていた。(cf. Google App Engine でダイエット表の作成 (3))
しかし、時が経過し、そのインスタンス変数に int 型を設定するという別の処理を追加し、それをコンストラクタから呼出してしまった。そのため、その後のフォーマットの検証でエラーが表示された。 int 型に変換した後のインスタンス変数に対して isdigit() を呼んでしまったためだ。
同じようなことが「Ruby でも型チェック (Codelogy)」に書かれていた。
例えば、Ruby でプログラムを書いていて、次のようなバグに悩まされたことのある人は多いのではないでしょうか。
- Integer オブジェクトを参照しているべき変数が、他の型のオブジェクトを参照している。
- そのオブジェクトが「いつ」「どこで」代入されたものなのか分からない。
この手のバグは、問題の発生 (不正な型の代入) と発覚 (エラーの発生) の位置が離れてしまうので、非常に厄介。 発生箇所を絞り込むのが難しいため、プログラムを広範囲に渡って見直すハメになります。
上記の引用中にある通り、「問題の発生と発覚の位置が離れていた」ので、当初エラーの原因が何なのかわからなかった。 (+_+)
ところで、Ruby には String クラスに isdigit() に相当するメソッドがないようだ。上記のサイトにあるように、Ruby では kind_of?() を使って型の検証をするのかな。 (cd. Object - Rubyリファレンスマニュアル ) それに相当するものとして、Pyhton では、3.6 types -- 組み込み型の名前, 組み込み関数の isinstance( object, classinfo) ということか。 (@_@)
int()
ちなみに int 型に変換する int() を上記のリストに適用した場合、
for i,e in enumerate(tests): try: print int(e) except Exception, e: print e, ':', "[%d]" % i, tests[i] print type(100)
結果は、
100 invalid literal for int() with base 10: '100.1' : [1] 100.1 invalid literal for int() with base 10: '' : [2] invalid literal for int() with base 10: 'hoge' : [3] hoge invalid literal for int() with base 10: '10hoge' : [4] 10hoge invalid literal for int() with base 10: 'hoge10' : [5] hoge10 invalid literal for int() with base 10: 'ho10ge' : [6] ho10ge 100
こちらは、文字列・数値なら OK のようだ。ただし、小数もはじくとは結構厳しい。 (@_@;)
Ruby の to_i
Ruby で int() に相当する to_i は動作が微妙に違う。
p "10".to_i p "10.1".to_i p "10hoge".to_i p "ho10ge".to_i p "hoge10".to_i p "".to_i p 100.to_i
結果は、
10 10 10 0 0 0 100
Ruby には、String、 Integer クラスに to_i があるから上記のような結果になる。できるだけ数値にできるものは数値にするという意気込みが感じられる。 ^^; しかし考えてみると、上記の空文字に対する to_i と以下の
p "0".to_i p 0.to_i
とした場合、すべて 0 となってしまい元が何だかわからなくなってしまう。 (+_+) こういう場合は、必要であれば kind_of? で検査、内容のチェックをするのかな。
数値に変換できるかどうかで処理を分けたいならキャストして例外を発生させたほうが扱いやすいと思います。
なるほど。
p Integer(0) p Integer("0") p Integer("")
とすると、
0 0 digittest.rb:19:in `Integer': invalid value for Integer: "" (ArgumentError) from digittest.rb:19
0コメント:
コメントを投稿