Errors and Exceptions
ここではエラーと例外について勉強します。
Syntax Errors
Syntax Errorはよく見るエラーですね。
>>> strings = ['1', '2', '3'] >>> for str in strings File "<stdin>", line 1 # 「:」が抜けている for str in strings ^ SyntaxError: invalid syntax
ちなみにSyntaxErrorではエラー検出箇所を「^」で教えてくれます。
Exceptions
例外!例外って知識としてはありますけど、使いこなすのってとっても難しいですよね。例外を使いこなしたコードはいかに美しいことか!C言語で育ったおじさんにはなかなか難しいのです。どうしても戻り値で返しちゃう癖が抜けない。
閑話休題。例外に関して分かりやすい定義があったので掲載しておきます。
Errors detected during execution are called exceptions
http://docs.python.org/tutorial/errors.html#exceptions
>>> 10 * (1 / 0) Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: integer division or modulo by zero >>> 4 + spam * 3 Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: cannot concatenate 'str' and 'int' objects
名前はXxxxxErrorですが、例外なんですね。それぞれの例外では例外の原因が表示されています。自分で例外を定義する時も同じシンタックスで説明を表示するのがよいですね。
Handling Exceptions
それでは例外をキャッチしてみましょう。
>>> while True: ... try: ... x = int(raw_input("Please enter a number: ")) ... print '=> ', x ... except ValueError: ... print "Oops! That was no valid number. Try again..." ... Please enter a number: 1 => 1 Please enter a number: 2 => 2 Please enter a number: 3 => 3 Please enter a number: a # aは数値じゃないのでValueError発生 Oops! That was no valid number. Try again.. Please enter a number: ^C # Ctrl-CはKeyboardInterruptを発生させるがキャッチしていないためwhileループから抜ける Traceback (most recent call last): File "<stdin>", line 3, in <module> KeyboardInterrupt
またその他全ての例外をキャッチすることも出来ます。
>>> import sys >>> try: ... f = open('myfile.txt') ... s = f.readline() ... i = int(s.strip()) ... except IOError: ... print "I/O error" ... except ValueError: ... print "Could not convert data to an integer." ... except: # ←ここ ... print "Unexcepted error:", sys.exc_info()[0] ... raise
またJavaのfinally節とちょっと違いますが、try節で例外が発生しなかった時に実行させる節を定義することが出来ます。
import sys for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
このスクリプト (exception_example.py) を実行すると
% python2.5 exception_example.py % python2.5 exception_example.py exception_example.py exception_example.py has 10 lines % python2.5 exception_example.py ddd cannot open ddd
確かに例外が発生したらelse:節は実行されませんね。
Raising Exceptions
これまでも出てきましたが、自分で例外を発生させるにはraiseという構文を使います。
>>> try: ... raise NameError('HiThere') # 例外の送信 ... except NameError: ... print 'An exception flew by!' ... raise # 例外の再送 ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in <module> NameError: HiThere
またいわゆる「例外の再送」は引数なしでraiseとします。
User-defined Exceptions
では自分で例外を定義してみます。例外はExceptionクラスを継承して定義します (もちろん直接継承じゃなくてもOK)。
>>> class MyError(Exception): ... def __init__(self, value): # コンストラクタ ... self.value = value ... def __str__(self): # 文字列化メソッド ... return repr(self.value) ... >>> try: ... raise MyError(2 * 2) ... except MyError: ... print 'My exception occurred' ... My exception occurred
う〜ん、Python 2.5ではexcept構文のasが使えないんですよね。例外の引数にアクセスするにはどうしたらいいんだろうか?ということで調べてみたらこうでした。
>>> try: ... raise MyError(2 * 2) ... except MyError, e: # ← ここ! ... print 'My exception occurred, value:', e.value ... My exception occurred, value: 4
例外を作成するにあたってのベストプラクティスはこんな感じ。
- モジュール内でベースとなる例外クラスを作成
- 個々の例外は↑のベース例外クラスを継承して作成
また、例外の名前は「Error」という文字列が最後に付けるのがよいようです。「Exception」の方が分かりやすいような気が…。
Defining Clean-up Actions
やっぱりありました、finally。意味もJavaと全く同じですね。処理の最後に必ず実行されます。
>>> try: ... raise KeyboardInterrupt ... finally: ... print 'Goodby, world!' ... Goodby, world! Traceback (most recent call last): File "<stdin>", line 2, in <module> KeyboardInterrupt
もちろんexcept:ブロックの中で例外が投げられても問題ありません。
>>> try: ... raise KeyboardInterrupt ... except KeyboardInterrupt: ... print 'KeyboardInterrupt occurred' ... raise ... finally: ... print 'Goodby, world!!' ... KeyboardInterrupt occurred Goodby, world!! Traceback (most recent call last): File "<stdin>", line 2, in <module> KeyboardInterrupt
Predefined Clean-up Actions
こちらはC#でいうところのusing節みたいなものですね。ちなみにC#ではこんな感じに書きます。
using (StreamReader reader = File.OpenText(@"c:\sample.txt")) { Console.WriteLine( reader.ReadLine()); }
Pythonではこのように書きます…と思ったら、with文を2.6からの機能でした。
>>> with open("workfile.txt") as f: <stdin>:1: Warning: 'with' will become a reserved keyword in Python 2.6 File "<stdin>", line 1 with open("workfile.txt") as f: ^ SyntaxError: invalid syntax