第4回 Python勉強会@旭川 エラーと例外

Python勉強会@旭川です。もう4回目なんですな。

第4回 Python勉強会@旭川 – connpass

今回は割と読んだつもりだったのですが、
raiseで例外を送出できるのが、インスタンスだけではなくて、
クラスでもいいってのは見落としていたので、気がつけてよかったです。

というわけで、今日のメモ。

エラーと例外

errorは2つの種類がある。

  • 構文エラー
  • 例外

構文エラー

pythonを使い始めのころは、インデントがずれてたり、コロンなかったりしてよくSyntaxErrorがでる

例外

例外とは構文上は問題ないが実行中にエラーが発生して処理が止まってしまうあれ。

例えば

  • ゼロで割ってしまった。
  • 定義していない名前を使った
  • 型の異なる(評価する方法がない)オブジェクトを評価してしまった

もうちょいよくありそうなのだと

  • ファイルを開こうとしたらファイルがなかった
  • ファイルに書き込んだら書き込めなかった。
  • メモリを確保しようとしたら足りなかった
  • とあるサーバーに接続したかったが繋がらなかった。

というわけで、例外は適当なタイミングで適切に処理しましょう。

例外はtry…exceptで補足することが可能

try:
    do_something()
except:
    pass    # 例外を補足した場合ここを実行
else:
    pass    # 例外が何も起きなかった場合ここを実行
finally:
    pass    # 最後に必ず実行

同時に複数の例外を指定することが可能

except (RuntimeError, TypeError, NameError):
    pass

exceptでは例外名の後に変数を指定することで、例外インスタンスにアクセスできる。

try:
    raise Exception('spam', 'eggs')
except Exception as inst:
    print type(inst)
    print inst.args
    print inst
    x, y = inst
    print 'x = ', x
    print 'y = ', y
<type 'exceptions.Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

例外の情報はsys.exc_info()でも取得可能

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as (errno, strerror):
    print "I/O error({0}): {1}".format(errno, strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

ちなみに例外の変数の書き方はいくつか種類がある(あった)。

1. Exception as e

try:
    f = open('hogehoge')
except IOError as e:
    print e
  1. Exception, e

たぶんこれは古い書き方・・・だと思う。

try:
    f = open('hogehoge')
except IOError, e:
    print e
  1. sys.exc_info()

この場合、[0]がtype, [1]がinstance (exceptions.IOError), [2]にtracebackが格納

try:
    f = open('hogehoge')
except IOError:
    for e in sys.exc_info():
        print type(e)
        print e
<type 'type'>
<type 'exceptions.IOError'>
<type 'exceptions.IOError'>
[Errno 2] No such file or directory: 'hoge'
<type 'traceback'>
<traceback object at 0x101f10d88>
  1. Exception, (errno, msg):

これは1や2と同じだが、ExceptionがErrnoとメッセージのタプルを返すので、
それをそのまま入れている形。

try:
    f = open('hogehoge')
except IOError, (errno, msg):
    print errno
    print msg

例外の送出、再送出

raise文で例外を送出することが可能。

except節の場合、発生している例外を再送出することが可能

try:
    raise Exception()
except:
    print 'hogehoge'
    raise # 再送出 呼び出し側にException()が到達する

raise はクラスでもいい。
でも引数が必要な例外はだめ。

raise Exception

ユーザ定義の例外

通常のクラスの継承と同じで、Exceptionを継承して作ればよい。

class MyError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

勉強会では「Exceptionのソースはみてないけどーーー」って話があった気がしますが、
Exceptionのソースはたぶんこれ Python-2.7.3/Objects/exceptions.c

with

ファイルをオープンしたら必ずクローズさせたいときに便利な文

try:
    f = open('hoge')
    f.read()
finally
    f.close()

上の処理が下の処理と同じ感じ

with open('hoge') as f:
    f.read()

f.close()は目に見えないけど、裏でやってくれている。

さらに複数の要素も使える。

with A() as a, B() as b:
    print a, b

withを作る

withは__enter__()メソッドと__exit__()メソッドを実装していれば使える。

with開始時に__enter__が呼ばれ、その戻り値が渡される。

with文を抜けるときに__exit__が呼ばれる。

class MyClass(object):
    def __init__(self, msg):
        self.msg = msg
    def __enter__(self):
        print "enter"
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        print "exit"
        print exc_type
        print exc_value
        print traceback

with MyClass('hogehoge') as m:
    print m.msg
enter
hogehoge
exit
None
None
None

次回

さて、次回は私が進行役をやります。
次回はpythonチュートリアルの9.クラスです。
途中から参加でも全然大丈夫ですので、興味のある方は学生さんでも社会人さんでも人妻さんでもどうぞー。