最終投稿日:2022年6月18日
Python基本(コンテキストブロック)
コンテキストブロック
ファイルやネットワーク接続を行う処理をする場合、それらのリソースは適切な後処理が必要となります。(切断等)
その様な処理には[with]ブロックを利用したコンテキスト制御が便利です。
例)
その様な処理には[with]ブロックを利用したコンテキスト制御が便利です。
例)
class myContext:
def __enter__(self):
print('ネットワークに接続等...')
def __exit__(self, exc_type, exc_val, exc_tb):
print('ネットワーク切断等...') with myContext():
※with ブロックで myContext クラスを指定すると以下の様な処理となる
print('~ メイン処理 ~') ※ネットワークに接続等...
※~ メイン処理 ~
※ネットワーク切断等...
【メモ】def __enter__(self):
print('ネットワークに接続等...')
def __exit__(self, exc_type, exc_val, exc_tb):
print('ネットワーク切断等...') with myContext():
※with ブロックで myContext クラスを指定すると以下の様な処理となる
print('~ メイン処理 ~') ※ネットワークに接続等...
※~ メイン処理 ~
※ネットワーク切断等...
__exit__メソッドの各引数は例外が発生した際の値を受け取る
例)
例)
myList = [ 1, 2, 3 ]
print( myList[ 3 ] ) ※4番目はないのでエラーとなる
この時各引数の内容を print すると以下の様になる
exc_type ⇒ <class 'IndexError'>
exc_val ⇒ list index out of range
exc_tb ⇒ <traceback object at 0x7f0e04d88588> [exc_tb]はトレースバックオブジェクトなので、更に詳細な内容を確認する事ができる
print( myList[ 3 ] ) ※4番目はないのでエラーとなる
この時各引数の内容を print すると以下の様になる
exc_type ⇒ <class 'IndexError'>
exc_val ⇒ list index out of range
exc_tb ⇒ <traceback object at 0x7f0e04d88588> [exc_tb]はトレースバックオブジェクトなので、更に詳細な内容を確認する事ができる
print(exc_tb.tb_frame) ⇒ <frame object at 0x7ffbb2023438>
※このレベルのフレームオブジェクト
print(exc_tb.tb_lasti) ⇒ 68
※最後に実行しようとしたバイトコード中のインストラクションを示すインデックス
print(exc_tb.tb_lineno) ⇒ 26
※現在の Python ソースコードの行番号
exc_tb = exc_tb.tb_next
※このオブジェクトの内側 (このレベルから呼び出された) のトレースバックオブジェクトを格納
※存在しない場合は bool( exc_tb ) == False となる
※exc_tb.tb_next を使って何重にも階層のあるトレースバックも while 文などで制御できるが
そもそもエラーとなったソースコード等も確認できず不便である。
そこでライブラリ inspect を利用すると良い
例)
※このレベルのフレームオブジェクト
print(exc_tb.tb_lasti) ⇒ 68
※最後に実行しようとしたバイトコード中のインストラクションを示すインデックス
print(exc_tb.tb_lineno) ⇒ 26
※現在の Python ソースコードの行番号
exc_tb = exc_tb.tb_next
※このオブジェクトの内側 (このレベルから呼び出された) のトレースバックオブジェクトを格納
※存在しない場合は bool( exc_tb ) == False となる
※exc_tb.tb_next を使って何重にも階層のあるトレースバックも while 文などで制御できるが
そもそもエラーとなったソースコード等も確認できず不便である。
そこでライブラリ inspect を利用すると良い
例)
import inspect ※ライブラリのインポート
def showPrint( l ):
print(l[3]) ※引数のリストに4番目はないためエラーとなる class myContext:
with hoge: myList = [ 1, 2, 3 ]
showPrint(myList) ※メソッドを処理する 【実行結果】
def showPrint( l ):
print(l[3]) ※引数のリストに4番目はないためエラーとなる class myContext:
def __enter__(self):
print('ネットワークに接続等...') def __exit__(self, exc_type, exc_val, exc_tb):
print( exc_type )
print( exc_val )
print( '------------' )
exc_tb = inspect.getinnerframes( exc_tb )
※トレースバックオブジェクトに対しライブラリの getinnerframes メソッドを使う
for frame_object,filename,lineno,function,code_context,index in exc_tb:
※for 文で6個の値を受け取れ、更に全てのトレースバックの階層をループする
print(frame_object)
print(filename)
print(lineno)
print(function)
print(code_context)
print(index)
print( '------------' )
print('ネットワーク切断等...')
hoge = myContext()print('ネットワークに接続等...') def __exit__(self, exc_type, exc_val, exc_tb):
print( exc_type )
print( exc_val )
print( '------------' )
exc_tb = inspect.getinnerframes( exc_tb )
※トレースバックオブジェクトに対しライブラリの getinnerframes メソッドを使う
for frame_object,filename,lineno,function,code_context,index in exc_tb:
※for 文で6個の値を受け取れ、更に全てのトレースバックの階層をループする
print(frame_object)
print(filename)
print(lineno)
print(function)
print(code_context)
print(index)
print( '------------' )
print('ネットワーク切断等...')
with hoge: myList = [ 1, 2, 3 ]
showPrint(myList) ※メソッドを処理する 【実行結果】
ネットワークに接続等...
list index out of range
------------ /home/guest/python15h/chapter01/myContext.py
28 [' showPrint(myList)\n']
0
------------ /home/guest/python15h/chapter01/myContext.py
4
showPrint
[' print(l[3])\n']
0
------------
ネットワーク切断等...
------------ /home/guest/python15h/chapter01/myContext.py
28 [' showPrint(myList)\n']
0
------------ /home/guest/python15h/chapter01/myContext.py
4
showPrint
[' print(l[3])\n']
0
------------
ネットワーク切断等...
contextlibを使ったコンテキストマネージャの作成
[contextlib]ライブラリを使用すると、前述のコンテキストブロックの定義の様にクラスを使わずにジェネレータで行えます。
例)
例)
import inspect
import contextlib
import sys
@contextlib.contextmanager ※「@」でコンテキストマネージャを定義する
def myContextLib(): ※ジェネレータと同じ要領
try:
print('enter...') ※前処理を担当するブロック
yield ※yield 以降が後処理を担当する
except: ※例外処理は except に記述する
ex, ms, exc_tb = sys.exc_info() ※sys.exc_info() メソッドでトレースバックオブジェクトを取得できる
print('error...')
exc_tb = inspect.getinnerframes( exc_tb ) ※前述の処理と同じ
for frame_object,filename,lineno,function,code_context,index in exc_tb:
print( '------------' )
print(frame_object)
print(filename)
print(lineno)
print(function)
print(code_context)
print(index)
print( '------------' )
finally:
print('exit...')
with myContextLib():
myList = [ 1, 2, 3 ] showPrint(myList) ※前述の関数を利用する 【実行結果】
enter... ※前処理
error... ※例外発生
------------ /home/guest/python15h/chapter01/myContext.py
39
myContextLib
[' yield\n']
0
------------
------------ /home/guest/python15h/chapter01/myContext.py
58 [' showPrint(myList)\n']
0
------------
------------ /home/guest/python15h/chapter01/myContext.py
6
showPrint
[' print(l[3])\n']
0
------------
exit... ※後処理
import contextlib
import sys
@contextlib.contextmanager ※「@」でコンテキストマネージャを定義する
def myContextLib(): ※ジェネレータと同じ要領
try:
print('enter...') ※前処理を担当するブロック
yield ※yield 以降が後処理を担当する
except: ※例外処理は except に記述する
ex, ms, exc_tb = sys.exc_info() ※sys.exc_info() メソッドでトレースバックオブジェクトを取得できる
print('error...')
exc_tb = inspect.getinnerframes( exc_tb ) ※前述の処理と同じ
for frame_object,filename,lineno,function,code_context,index in exc_tb:
print( '------------' )
print(frame_object)
print(filename)
print(lineno)
print(function)
print(code_context)
print(index)
print( '------------' )
finally:
print('exit...')
with myContextLib():
myList = [ 1, 2, 3 ] showPrint(myList) ※前述の関数を利用する 【実行結果】
enter... ※前処理
error... ※例外発生
------------ /home/guest/python15h/chapter01/myContext.py
39
myContextLib
[' yield\n']
0
------------
------------ /home/guest/python15h/chapter01/myContext.py
58 [' showPrint(myList)\n']
0
------------
------------ /home/guest/python15h/chapter01/myContext.py
6
showPrint
[' print(l[3])\n']
0
------------
exit... ※後処理
デコレータ
デコレータとは関数の前後に処理を追加する処理です。
例)
例)
def myDec(func): ※デコレータを定義
def wrap():
print('enter...')
func()
print('exit...')
return wrap def myHello(): ※通常の関数
print('Hello...') h = myDec(myHello) ※デコレータを適用する
print(h()) 【実行結果】
enter...
Hello...
exit...
def wrap():
print('enter...')
func()
print('exit...')
return wrap def myHello(): ※通常の関数
print('Hello...') h = myDec(myHello) ※デコレータを適用する
print(h()) 【実行結果】
enter...
Hello...
exit...
● デコレータの文法
デコレータ専用の文法を使うと簡単に設定できます。
前述のデコレータを定義したソースを利用し、以下の様に簡潔に記述できます。
例)
前述のデコレータを定義したソースを利用し、以下の様に簡潔に記述できます。
例)
@myDec ※この関数にはデコレータ[myDec]が適用されている
def myHello():
print('Hello...')
print(myHello())
def myHello():
print('Hello...')
print(myHello())
● 引数を持ったデコレータ
例)
def mySumDec(func):
def wrap( *args ): ※【1】に合わせ複数の引数を受け付ける
print('前処理...')
myint = func( *args ) ※【1】に合わせ複数の引数を受け付ける
print(myint)
print('後処理...')
return wrap @mySumDec
def mySum( *args ): ※【1】複数の引数を受け付ける
mylist = list( args )
return sum(mylist) print(mySum(1,2,3))
def wrap( *args ): ※【1】に合わせ複数の引数を受け付ける
print('前処理...')
myint = func( *args ) ※【1】に合わせ複数の引数を受け付ける
print(myint)
print('後処理...')
return wrap @mySumDec
def mySum( *args ): ※【1】複数の引数を受け付ける
mylist = list( args )
return sum(mylist) print(mySum(1,2,3))
● 関数内のドキュメントをコピーする
デコレータを設定するとその関数内に記述したコメントが「関数.__dec__」で参照できなくなる。
これを防ぐには[unctools]クラス[wraps]メソッドを使う
例)
これを防ぐには[unctools]クラス[wraps]メソッドを使う
例)
import functools ※インポートする
def mySumDec(func):
@functools.wraps(func) ※デコレータ設定さきのコメントをコピーする
def wap( *args ):
print('前処理...')
myint = func( *args )
print(myint)
print('後処理...')
return wap @mySumDec
def mySum( *args ):
"""まいさむ""" ※本来の関数にコメントを記述
mylist = list( args )
return sum(mylist) print(mySum.__doc__) ※「まいさむ」が表示される
def mySumDec(func):
@functools.wraps(func) ※デコレータ設定さきのコメントをコピーする
def wap( *args ):
print('前処理...')
myint = func( *args )
print(myint)
print('後処理...')
return wap @mySumDec
def mySum( *args ):
"""まいさむ""" ※本来の関数にコメントを記述
mylist = list( args )
return sum(mylist) print(mySum.__doc__) ※「まいさむ」が表示される




