Lightweight Language Weekend ls-lRシェル Iteratorを使おう 日本Pythonユーザ会 石本 敦夫 Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル Iterator パターン • GoFによる定義 集約オブジェクトが基にある内部表現を公開せず に、その要素に順にアクセスする方法を提供する Iterator Collection Item Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル Pythonのイテレータ • データやアルゴリズムをイテレータでラップし、 シーケンスのように順にデータを取得する方 法を提供する Iterator Collection Function File etc,... Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル Pythonのイテレータ サポート • Pythonのステートメントはイテレータを使用するよう に変更されている Python2.1以前 Python2.2以降 for i in [1,2,3]: print i seq = [1,2,3] n=0 while True: try: print seq[n] except IndexError: break n += 1 iterator = [1,2,3].__iter__() while True: try: i = iterator.next() except StopIteration: break print i i, j = (1,2) seq = (1,2) i = seq[0] j = seq[1] iterator = (1,2).__iter__() i = iterator.next() j = iterator.next() Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル イテレータインターフェース • Iterable - イテレート可能オブジェクト – 組み込み関数 iter()を使ってイテレータを取得することが できるオブジェクト – for文などで直接使用することができる – Python組み込みのコンテナはすべてIterable – イテレータを返すメソッド__iter__() を持つ • iterator – イテレータオブジェクト – 次の値を返すnext()メソッドを持つ – next()で返すオブジェクトがなければ、StopIteration例 外を送出する – __iter__()メソッドを持ち、自分自身を返す Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル イテレータの例 • Fileイテレータ - ファイルを行のシーケンスとみなし、 順に読み込む inFile = open("./foo.txt") for line in inFile: print line # 又は inFile = open("./foo.txt") fileIter = iter(inFile) print fileIter.next() print fileIter.next() Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル イテレータの実装例 class AlterIter: '''seq1, seq2内の要素を交互に返すイテレータ''' def __init__(self, seq1, seq2): self._seqs = (seq1, seq2) self._cur = 0 self._max = min(len(seq1), len(seq2)) # 最大インデックス値 def __iter__(self): return self def next(self): n, idx = self._cur % 2, self._cur//2 if idx >= self._max: raise StopIteration # iterate終了 ret = self._seqs[n][idx] • 通常、__init__(), __iter__(), next()は self._cur += 1 最低限必要 return ret • 次回の呼び出しに備えて状態を保存し なければならない Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル AlterIterの使用方法 • for ステートメントでイテレータを使用する for v in AlterIter([1,2],['a','b']): print v, 出力 => 1 a 2 b • もちろん、next()メソッドで順次読み出しても良い values = AlterIter([1,2],['a','b']) print values.next(), print values.next(), print values.next(), print values.next(), 出力 => 1 a 2 b Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル イテレータのメリット • 実際にシーケンスを作成する必要がないため 効率が良い • 無限長のシーケンスを扱う事ができる • 呼び出し元の処理がシンプルになる – データを取り出すタイミングを呼び出し元で制御 できる – forループ内で終了条件をチェックする必要がな いため、処理がすっきりする Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル Generator関数 • 関数定義の形でイテレータを定義 • Generator関数は戻り値としてイテレータを返す • イテレータのnext()は、yield文で指定した値を次々 に返す # return ('最初', '2番目', '終わり' )と同じ def gen(): yield '最初' yield '2番目' yield '終わり' # Generetor関数 の呼び出し v = gen() print v.next(), # '最初' print v.next(), # '2番目' print v.next(), # '終わり' # 又は for v in gen(): print v, # 出力 => 最初 2番目 終わり Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル Generatorの動作 • 呼び出されると イテレータの一種であるgeneratorオブジェ クトを返す • generatorのnext()メソッドを呼び出すと、関数のyield文ま でを実行してその値を返す • yield文に遭遇せずに関数が終了すると、StopIteration例 外を送出する def gen(): yield 'a' yield 'b' 同じ class gen: def __init__(self): self._n = 0 def __iter__(self): return self def next(self): if self._n == 0: return 'A' if self._n == 1: return 'B' raise StopIteration Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル Generatorのメリット • 簡単にイテレータを作成する事ができる • 処理状態をどこかに退避する必要がない • 速い! – クラスを使ったイテレータより100%以上速いケー スも – ほとんどローカル変数のみで実行できる – 状態を退避する必要がない Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル Generator版AlterIter def AlterIter(seq1, seq2): _max = min(len(seq1), len(seq2)) for idx in range(_max): yield seq1[idx] yield seq2[idx] • イテレータクラスより簡単 • 高速! Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル Fibonacci数列 def fibonacci(): i=j=1 yield i yield j while True: i, j = j, i+j yield j for n in fibonacci(): print n, 出力 => 1 1 2 3 5 8 13 21 34 55 89 ... Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル イテレータの合成 def odds(iterable): '''奇数のみを返すイテレータ''' for v in iterable: if v % 2: yield v def lessThan(iterable, cond): '''cond以下の値のみを返すイテレータ''' for v in iterable: if v < cond: yield v seq = [90, 22, 49, 93, 49, 68, 36, 96, 31, 23] for v in odds(lessThan(seq, 50)): print v, Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル パフォーマンス比較 def test1(seq): for v in lessThan(odds(seq), 50): pass def test2(seq): for v in seq: if (v % 2) and (v < 50): pass def test3(seq, f): for v in seq: if f(v): pass # テスト用シーケンス seq = [random.randint(0, 10000) for v in range(1000000)] test1(seq) test2(seq) test3(seq, lambda v: (v < 50) and (v % 2)) 測定環境: Python2.3.3 Windows2000 結果 test1() 0.72秒 test2() 0.59秒 test3() 1.08秒 •イテレータの二段重ねでもそれほど遅くはな らない •柔軟性とのトレードオフ Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル 参考リンク • PEP 234 Iterators http://www.python.org/peps/pep-0234.html • PEP 255 Simple Generators http://www.python.org/peps/pep-0255.html Copyright c 2004 Python Japan User's Group. Lightweight Language Weekend ls-lRシェル 終わりに • イテレータを使おう。イテレータは Pythonicだ。 • Generatorを使おう。Generatorは 魔法じゃない。 Copyright c 2004 Python Japan User's Group.
© Copyright 2024 ExpyDoc