06. 例外処理

処理の途中で異常を検出した時に、異なる処理をしたいのが例外処理です。サンプルコードを記述してみて、Pythonの例外処理は少し癖がありそうな感触でした。

--- tcsh

残念ながら、該当する文法が(たぶん)ありません。個々の処理において、注意深く条件確認記述をしていくことで例外が起きづらくなると思いますが、想定外の問題が発生することを想定するならば、その部分の処理はtcshに任せない方が無難かと思います。

--- Perl

文法的には以下のようになります。例外が起きそうな処理を(*1)に記述します。例外発生時の処理を(*2)に記述します。例外が発生しなかった時の処理を(*3)に記述します。

eval {
    *1
};
if($@){
    *2
}else{
    *3
}

例外処理が発生しても、後続処理「hoge」が表示されます。

#!/usr/bin/perl
use warnings;
print "Please input 2 decimals, separate with space\n";
$vars = <STDIN>;
($a,$b) = split(/\s/,$vars);
eval {
  $result = $a/$b;
};
if($@){
  print "failed to calculate...\n";
  print "--- Error message is following...\n";
  print "$@";
  print "---\n";
}else{
  print "result = " . $result  . "\n";
}
print "hoge\n";

--- Ruby

文法的には以下のようになります。例外が起きそうな処理を(*1)に記述します。(*1)の処理中に例外が発生すると(*2)が呼ばれます。例外が発生しなければ(*3)が呼ばれます。例外に関係なく、最後に(*4)が呼ばれます。

begin
    *1
rescue
    *2
else
    *3
ensure
    *4
end

例外処理のレベルによりますが、簡単に使うなら以下のように書いてもいいと思います。

begin
    *1
rescue
    *2
end

例外に関係なく、終了処理が必須であるならば、ensureを使いましょう。

以下は、標準入力から2つの値を入れて除算するサンプルです。getsで取り込んだ値は文字列のため、決め打ちでint型に変換しています。そのため、1/2を計算するとint型なので0が返りますが、ここは計算精度を求めていないので、これでいいかなと。なお、標準入力をfloat変換すると、0で割っても例外が発生しません。

#!/usr/bin/ruby -w
puts "Please input 2 decimals, separate with space"
a,b = STDIN.gets.chomp.split(/\s/)
begin
  result = a.to_i / b.to_i
rescue
  puts "failed to calculate..."
  puts "--- Error message is following..."
  puts $!
  puts "---"
else
  puts "result = #{result}"
ensure
  puts "kokoha kanarazu yobareru..."
end
puts "hoge"

*1 STDIN.gets、はgetsだけでもいいのですが、他のファイルハンドルを使ったあとでgetsだけ呼び出すとRubyがそのハンドル経由でgetsを呼ぼうとするため、確実に動作させることを考慮してSTDIN.gets、と書いています

また、例外処理を2-1.「ディレクトリとファイルサーチ」のRubyサンプルコードの中で使っています。「rescue」でサーチしてみてください。このコードでは、ファイルの中を検索する中で、想定外の文字コードにぶつかったときに例外処理を行っています。例外処理を記述しないと、RubyがErrorを出力して落ちるため、「どのファイルの何行目」で以上が起きたかを出力するようにしています。

--- Python

文法的には以下のようになります。例外が起きそうな処理を(*1)に記述します。例外発生時の処理を(*2)に記述します。例外に関係なく、最後の処理が必要なら(*3)に記述します。

try:
    *1
except:
    *2
finally:
    *3

以下は失敗例です。例外処理後、「hoge」を表示させようとしていますが…

#!/usr/bin/python
import re
print "Please input 2 decimals, separate with space"
vars = raw_input()
a,b = re.split('\s', vars)
try:
    result = int(a)/int(b)
    print "result = %d" % (result)
except:
    print "failed to calculate..."
    raise
finally:
    print "kokoha kanarazu yobareru..."
print "hoge"

例外を発生させると、exceptブロックとfinallyブロックが処理されたあと、その下の「hoge」を表示させることなく終わってしまいます。

Please input 2 decimals, separate with space
1 0
failed to calculate...
kokoha kanarazu yobareru...
Traceback (most recent call last):
  File ".//illegal.py", line 10, in <module>
    result = int(a)/int(b)
ZeroDivisionError: integer division or modulo by zero

例外が発生する処理ブロックをメソッドでラッピングして、それを例外制御ブロックで呼んであげると、例外処理のあとに続くコードが処理されます。

#!/usr/bin/python
import re
def exception_chk(var0,var1):
    result = int(a)/int(b)
    print "result = %d" % (result)
########################
print "Please input 2 decimals, separate with space"
vars = raw_input()
a,b = re.split('\s', vars)
try:
    exception_chk(a,b)
except:
    print "failed to calculate..."
print "hoge"