With文の基本動作
拙著「Pythonでデータ分析」においてwith文を用いているが、その動作を簡単に確認してみる。
With文の形式は、以下のようである。
with オブジェクト生成式 as target: スィート
オブジェクト生成式において用いられるクラス型は、2つのメソッド__enter__(self)と__exit__(self, p0, p1, p2)が宣言されていなければならない。
メソッド__enter__(self)は、with文のスィート(すなわちブロック)の実行前に呼び出される。その戻り値はtargetに設定される。
メソッド__exit__(self, p0, p1, p2)は、with文のスィートの実行後に呼び出される。パラメータp0, p1, p2は、スィート内で例外が生成されたときの情報が設定され、それらの値は__exit__内で例外処理を行うときに用いられる。まず、例外処理を行わない例を示し、その後、例外処理を行う例を示す。
With文用の簡単なクラス型を以下のように用意してみた。
class MyClass:
def __init__(self, mymsg):
self.mymsg = mymsg
def __enter__(self):
print('開始します。')
return self.mymsg
def __exit__(self, p0, p1,
p2):
print('終了します。')
関数__enter__の実行時に、「開始します。」が出力され、クラス型のオブジェクト生成時のパラメータmymsgが関数__enter__の関数値としてwith文のtargetに設定される。
関数__exit__は、「'終了します。’」の出力のみを行う。
上のクラス型MyClassを用いるwith文を次のように書いてみた。
with MyClass('例です。') as msg:
print('処理中です。')
print(msg)
このコードを実行すると以下の出力が得られる。
開始します。
処理中です。
終了します。
例です。
関数__enter__が実行されたときの出力「開始します。」、with文のスイートのprint('処理中です。')による出力「処理中です。」、関数__exit__による出力「終了します。」、with文終了後ののprint(msg)による出力「例です。」が順番に出力されている。
上のサンプルプログラムを1つのスクリプトにまとめたものを、リスト1に示す。
リスト1
class MyClass:
def __init__(self, mymsg):
self.mymsg = mymsg
def __enter__(self):
print('開始します。')
return self.mymsg
def __exit__(self, p0, p1,
p2):
print('終了します。')
with MyClass('例です。') as msg:
print('処理中です。')
print(msg)
リスト2に例外処理を行うスクリプト例を示す。
リスト2 例外の処理
class MyClass:
def __init__(self,
mymsg=''):
self.mymsg = mymsg
def __enter__(self):
print('開始します。')
return self
def __exit__(self, p0, p1,
p2=None):
if
p2 == None:
print('終了します。')
else:
print('例外の発生')
def ShowMsg(self):
print(self.mymsg)
import sys
try:
with MyClass('例です。') as check:
a =
int(input('a = '))
b =
int(input('b = '))
c =
a / b
print('a/b =', c)
except:
print(*sys.exc_info())
check.ShowMsg()
print('スクリプト実行終了。')
割り算
c = a / b
において、b=0のときは、0による割り算の例外が生起する。このとき、with文のスィート部を抜け出すときにメソッド__exit__が呼び出されるとともに、except部が実行されて例外の処理が行われる。実行例を以下に示す。
開始します。
a = 1
b = 0
例外の発生
<class 'ZeroDivisionError'>
division by zero <traceback object at 0x00000201BE165900>
例です。
スクリプト実行終了。