エキスパートPythonプログラミング読書会03に参加しました
エキスパートPythonプログラミング読書会03 : ATND に参加して講師のお手伝いをしてきました。
2章の後半、以下の内容を行いました。
- イテレータとジェネレータ
- itertools モジュール
- デコレータ
- with と contextlib
最後にこんな質問がありました。
コンテキストプロバイダの実装方法が2通りあるが、デコレータで実装するか、with 文(プロトコル) で実装するか、どのように使い分ければ良いか?
@shimizukawa に聞いてみて、とても勉強になりました。with 文を使用するときの分かり易いポイントの1つとして処理対象がクラスであれば、ベースクラスを継承してサブクラスで __enter__ と __exit__ を実装することができます。わざわざクラスで実装するほどでも、、、というときはデコレータで実装するのも良いです。その際、デコレータで try...finally を書いても良いですが、contextlib.contextmanager を使えば yield で中断される前処理を __enter__、後処理を __exit__ として実装してくれるのでお手軽で良さそうというのが contextlib モジュールの項での紹介でした。本来の意図するジェネレータじゃないよねという指摘もありましたが(^ ^;;
読書会で使用したサンプルコードです。
- 07
- islice の使用例
def islice_with_stop(n): """ >>> islice_with_stop(5) 0 1 2 """ from itertools import islice data = range(n) for i in islice(data, 3): # data[:3] print i def islice_with_start_stop_step(n): """ >>> islice_with_start_stop_step(5) 1 3 """ from itertools import islice data = range(n) for i in islice(data, 1, n, 2): # data[1:n:2] print i
-
- tee の使用例
def tee_simple(n): """ >>> tee_simple(3) t1: 0 t1: 1 t1: 2 t2: 0 t2: 1 t2: 2 """ from itertools import tee data = range(n) t1, t2 = tee(data) for i in t1: print "t1:", i for i in t2: print "t2:", i def tee_careful(n): """ >>> tee_careful(3) data: 0 data: 1 t1: 2 t2: 2 """ from itertools import islice, tee data = islice(range(n), n) t1, t2 = tee(data) print "data:", next(data) print "data:", next(data) for i in t1: print "t1:", i for i in t2: print "t2:", i def tee_multiple(n): """ >>> tee_multiple(1) t1: 0 t2: 0 t3: 0 """ from itertools import islice, tee data = islice(range(n), n) t1, t2, t3 = tee(data, 3) for i in t1: print "t1:", i for i in t2: print "t2:", i for i in t3: print "t3:", i
-
- groupby の使用例
def groupby_without_sort(path): """ >>> groupby_without_sort("./group_input.txt") aaa ['aaa'] 1 bbb ['bbb'] 1 aaa ['aaa', 'aaa'] 2 bbb ['bbb'] 1 aaa ['aaa'] 1 ccc ['ccc'] 1 """ from itertools import groupby data = [line.rstrip() for line in file(path)] for key, grp in groupby(data): grp_data = [i for i in grp] print key, grp_data, len(grp_data) def groupby_with_sort(path): """ >>> groupby_with_sort("./group_input.txt") aaa ['aaa', 'aaa', 'aaa', 'aaa'] 4 bbb ['bbb', 'bbb'] 2 ccc ['ccc'] 1 """ from itertools import groupby data = [line.rstrip() for line in file(path)] data.sort() for key, grp in groupby(data): grp_data = [i for i in grp] print key, grp_data, len(grp_data) def groupby_with_keyfunc(path): """ >>> groupby_with_keyfunc("./group_input.txt") a ['aaa', 'aaa', 'aaa', 'aaa'] 4 b ['bbb', 'bbb'] 2 c ['ccc'] 1 """ from itertools import groupby data = [line.rstrip() for line in file(path)] data.sort() for key, grp in groupby(data, lambda x: x[0]): grp_data = [i for i in grp] print key, grp_data, len(grp_data)
- 08
- izip の使用例
def use_izip(key, data): """ >>> use_izip(["a", "b", "c"], [1, 2, 3]) ... print i [('a', 1), ('b', 2), ('c', 3)] ------------------------------ ('a', 1) ('b', 2) ('c', 3) """ from itertools import izip zipped_tupple = zip(key, data) print zipped_tupple print "-" * 30 for tup in izip(key, data): print tup def cnv_to_dict(key, data): """ >>> cnv_to_dict(["a", "b", "c"], [1, 2, 3]) {'a': 1, 'c': 3, 'b': 2} """ return dict(zip(key, data)) def cnv_to_dict_with_izip(key, data): """ >>> cnv_to_dict_with_izip(["a", "b", "c"], [1, 2, 3]) {'a': 1, 'c': 3, 'b': 2} """ from itertools import izip return dict(izip(key, data)) def read_enumerate(path): """ >>> read_enumerate("./test.txt") 3 """ cnt = 0 for cnt, line in enumerate(open(path, "r")): cnt += 1 # use cnt for something return cnt def read_count(path): """ >>> read_enumerate("./test.txt") 3 """ from itertools import count, izip cnt = 0 for cnt, line in izip(count(1), open(path, "r")): pass # use cnt for something return cnt
- 09
- シンプルなデコレータ
def bar(func): print '### I am bar' return func def foo(func): print '@@@ I am foo' return func @bar @foo def hoge(): """ >>> hoge() @@@ I am foo ### I am bar """ print '*** hoge' def fuga(): """ *** hoge >>> bar(foo(fuga))() @@@ I am foo ### I am bar +++ fuga """ print '+++ fuga'
- 10
- with でロックを扱った使用例
import sys from multiprocessing import Process, Lock def worker_with(lock, stream): with lock: stream.write("Lock acquired via with\n") def worker_no_with(lock, stream): lock.acquire() try: stream.write("Lock acquired try...fainaly\n") finally: lock.release() def main(): lock = Lock() w = Process(target=worker_with, args=(lock, sys.stdout)) t = Process(target=worker_no_with, args=(lock, sys.stdout)) w.start() t.start() w.join() t.join() if __name__ == "__main__": main()
-
- with でファイルを扱った使用例
def read_file(path, mode, charset="utf-8"): import io with io.open(path, mode, encoding=charset) as file_obj: for num, line in enumerate(file_obj): yield num + 1, line.rstrip("\r\n") def main(): from time import sleep path = "./test.txt" for num, line in read_file(path, mode="r"): print num, line sleep(1) while True: print "loop" sleep(1) if __name__ == "__main__": main()
- 11
- contextlib.nested の使用例
from contextlib import contextmanager, nested @contextmanager def make_context(name): print "entering:", name yield name print "exiting :", name def main(): with nested(make_context("ネスト"), make_context("だと"), make_context("...")) as (a, b, c): print "inside with statement:", a, b, c if __name__ == "__main__": main()
-
- contextlib.closing の使用例
from contextlib import contextmanager, closing from urllib import urlopen def main(): with closing(urlopen("http://www.dehenken.co.jp")) as response: print response.info() print response print "after with block: ", response if __name__ == "__main__": main()
-
- contextlib.closing の使用例(例外)
from contextlib import contextmanager, closing from urllib import urlopen def main(): try: with closing(urlopen("http://www.dehenken.co.jp")) as response: print response.info() raise RuntimeError('rasing RuntimeError') except Exception as err: print err print "after with block: ", response if __name__ == "__main__": main()