まめりうむ

WindowsからMacへ乗り換るためのネタをまとめたり好きなことをまとめたり。

*

Pythonのwith文

   

スポンサーリンク

このところ興味の矛先がPythonからRaspberry Piに移りつつあります…w

なので、最近はあんまり言語リファレンスを読んでないんですが、今まで読んだ中でかなり便利そうな文があったので忘れないうちにまとめておきます。

Pythonのwith文

それはwith文です。

ちょっと前に読んだリーダブルコードでも紹介されてたんですが、with文を使うとクリーンアップが必要なコードをシンプルに記述出来ます。

例えばファイルを操作する場合、よく

try:
	f = open('foo.txt', 'w')
	print 'file opened.'
finally:
	f.close()
	print 'file closed.'

#実行結果
file opened.
file closed.

こんな感じでtry〜finally文を使います。

これと同じことをwith文を使うと

with open('foo.txt', 'w') as f:
	print 'file opened.'

#実行結果
file opened.

#closeされてる?
>>> f
>>> <closed file 'foo.txt', mode 'w' at 0x10bebc810>

こんな感じで、わざわざclose()メソッドを呼び出さなくてもきちんとcloseされるのでスッキリと記述出来ます。

with文が使えるオブジェクト

言語リファレンスによると

with 文は、 __enter__() メソッドがエラーなく終了した場合には __exit__() が常に呼ばれることを保証します。

ということで、見方を変えるとwith文が使えるオブジェクトでは少なくとも__enter__()メソッドと__exit()__メソッドの両方が定義されている必要がありそうです。

(ただ、__enter__()メソッドと__exit__()メソッドが定義されていれば必ず使えるわけではありません。)

定義されているメソッドを調べる方法

__enter__()メソッドと __exit__()メソッドが定義されてるか調べるには組込み関数dir()が使えそうです。

with_able = set(['__enter__','__exit__'])

f = open('foo.txt', 'w')
builtin = set(dir(f))
f.close()

if len(withable - builtin) == 0:
	print 'can use with statement.'
else:
	print "can't use with statement."

#実行結果
can use with statement.

with文が使えるクラスを自作してみる

__enter__()メソッドと__exit__()メソッドの両方を定義すれば良いわけですが、with文を使えるようにするにはちょっとだけポイントがあります。

__enter__()メソッドのポイント

with文のas節で指定された変数に__enter__()メソッドの戻り値が代入されるためreturnが必要(通常はselfを返しておけば良いのかな?)。

__exit__()メソッド

self以外に引数を3つ取ります。

この3つの引数にはwith文のコードブロック中で例外が発生した時に値が代入され、正常終了した場合は全てNoneになります。

あと、with文のコードブロックで例外が発生した場合でも__exit__()メソッドの戻り値をTrueにしておくと例外はココで止まりwith文以降も処理されます。

__exit__()メソッドの戻り値を指定しない場合やFalseに設定した場合は例外がwith文の外に送出されるので処理はココで止まります。

言語リファレンスによると__exit__()は例外を再送出するべきじゃないとのことなのでTrueにしておくのが良さそうです。

with文が使えるクラスのサンプル

試しにwith文が使える割り算のクラスを作ってみます。

class DevideInt:

	def __init__(self):
		print '__init__'

	def __enter__(self):
		print '__enter__'
		return self

	def calc(self, dividend , divisor):
		quotient = dividend / divisor
		print '{0} / {1} = {2}'.format(dividend, divisor, quotient)

	def __exit__(self, exc_type, exc_value, traceback):
		print '__exit__'
		print 'exc_type : ', exc_type
		print 'exc_value: ', exc_value
		print 'traceback: ', traceback
		return True

こんな感じにして、わざと例外が出るようなコードを実行してみます。

>>> for i in range(3):
... 	with DevideInt() as d:
... 		d.calc(i + 1, i)

#実行結果
__init__
__enter__
__exit__
exc_type :  <type 'exceptions.ZeroDivisionError'>
exc_value:  integer division or modulo by zero
traceback:  <traceback object at 0x106fbf1b8>
__init__
__enter__
2 / 1 = 2
__exit__
exc_type :  None
exc_value:  None
traceback:  None
__init__
__enter__
3 / 2 = 1
__exit__
exc_type :  None
exc_value:  None
traceback:  None

最初で例外が発生してますが処理が継続出来てますね(計算結果がおかしいのはスルーしてくださいw)。

__exit__()メソッドに戻り値を指定しなかったりFalseにした場合の実行結果はこうなります。

>>> for i in range(3):
... 	with DevideInt() as d:
... 		d.calc(i+1, i)
... 

#実行結果
__init__
__enter__
__exit__
exc_type :  <type 'exceptions.ZeroDivisionError'>
exc_value:  integer division or modulo by zero
traceback:  <traceback object at 0x10e96d1b8>
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "<stdin>", line 8, in calc
ZeroDivisionError: integer division or modulo by zero

例外がwith文の外に送出されるので処理が止まってます。

with文のネスト

今のところ使う機会がないんですが、with文はネストも出来るみたいです。

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

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

と同じとのこと。

ネストが深くならないのは良いですね。使いどころがあればですが…w


伝統的なtry〜finally文も悪く無いですがコードが見やすくなるwith文が使える状況の時は積極的に使って行きたいです。

 - プログラミング

スポンサーリンク

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

  関連記事

JForexで約定履歴画面を自作する

MT4もそうなんですけど、JForexも多くの国内業者に比べて約定履歴が見にくい …

Pythonでニコ生アラートを作る(放送通知サーバへの接続情報を使ってsocket接続)

前回の続きです。 今回は、前回得られた放送通知サーバへの接続情報を使ってsock …

JForexでスタンドアローンアプリを自作する

前回まではJForexs採用業者で提供されているチャートツールへの追加機能として …

Sublime Text3でPython開発環境を整えるためにインストールしたパッケージと設定

Sublime Text3でPythonのお勉強をするのに役立ちそうなパッケージ …

JForexで注文画面を自作する(新規注文に有効期限を設定)

前回作成した指値・逆指値注文画面のときにまとめて作ってしまえば良かったんですが忘 …

JForexで注文画面を自作する(スプレッド表示と新規注文時のオプション)

前回までで成行注文と全決済が出来るようになったので、今回はもう少し使い勝手を良く …

Pythonチュートリアルを読みました

Pythonを勉強するにあたって、最初にPythonスタートブックという本を読み …

JForexで注文画面を自作する(成行注文と全決済)

まずは成行注文と全決済が出来るようにしたいと思います。 GUIのパーツはEcli …

JForexで注文画面を自作する(指値・逆指値注文)

前回までで一応成行注文に最低限欲しい機能は付けられましたので、今回は指値・逆指値 …

Pythonスタートブックを読みました

Sublime Text3きっかけで、前からちょっと触ってみたいなと思ってたスク …