iterator を使おう
DESCRIPTION
Iterator を使おう. 日本 Python ユーザ会 石本 敦夫. Iterator パターン. GoF による定義 集約オブジェクトが基にある内部表現を公開せずに、その要素に順にアクセスする方法を提供する. Iterator. Collection. Item. Python のイテレータ. データやアルゴリズムをイテレータでラップし、シーケンスのように順にデータを取得する方法を提供する. Iterator. Collection. Function. File. etc,. Python のイテレータ サポート. - PowerPoint PPT PresentationTRANSCRIPT
Copyrightc2004Python JapanUser'sGroup.
Lightweight Language Weekend ls-lRシェル
Iterator を使おう
日本 Python ユーザ会 石本 敦夫
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
Iterator パターン• GoF による定義
集約オブジェクトが基にある内部表現を公開せずに、その要素に順にアクセスする方法を提供する
CollectionIterator
Item
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
Python のイテレータ• データやアルゴリズムをイテレータで
ラップし、シーケンスのように順にデータを取得する方法を提供する
CollectionIterator
Function
File
etc,...
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
Python のイテレータ サポート
Python2.1 以前 Python2.2 以降for i in [1,2,3]: print i
seq = [1,2,3]n = 0while 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()
• Python のステートメントはイテレータを使用するように変更されている
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
イテレータインターフェース• Iterable - イテレート可能オブジェクト
– 組み込み関数 iter() を使ってイテレータを取得することができるオブジェクト
– for 文などで直接使用することができる– Python 組み込みのコンテナはすべて Iterable– イテレータを返すメソッド __iter__() を持つ
• iterator – イテレータオブジェクト– 次の値を返す next() メソッドを持つ– next() で返すオブジェクトがなければ、 StopIteration
例外を送出する– __iter__() メソッドを持ち、自分自身を返す
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
イテレータの例• File イテレータ - ファイルを行のシーケンスと
みなし、順に読み込むinFile = open("./foo.txt")for line in inFile: print line
# 又はinFile = open("./foo.txt")fileIter = iter(inFile)print fileIter.next()print fileIter.next()
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
イテレータの実装例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] self._cur += 1 return ret
• 通常、 __init__(), __iter__(), next() は最低限必要
• 次回の呼び出しに備えて状態を保存しなければならない
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
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
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
イテレータのメリット• 実際にシーケンスを作成する必要がない
ため効率が良い• 無限長のシーケンスを扱う事ができる• 呼び出し元の処理がシンプルになる
– データを取り出すタイミングを呼び出し元で制御できる
– for ループ内で終了条件をチェックする必要がないため、処理がすっきりする
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
Generator 関数• 関数定義の形でイテレータを定義• Generator 関数は戻り値としてイテレータを返す• イテレータの next() は、 yield 文で指定した値を次々に返す
# Generetor 関数 の呼び出しv = gen()print v.next(), # ' 最初 'print v.next(), # '2 番目 'print v.next(), # ' 終わり '# 又はfor v in gen(): print v,
# 出力 => 最初 2 番目 終わり
# return (' 最初 ', '2 番目 ', ' 終わり ' ) と同じ
def gen(): yield ' 最初 ' yield '2 番目 ' yield ' 終わり '
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
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
同じ
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
Generator のメリット• 簡単にイテレータを作成する事ができる• 処理状態をどこかに退避する必要がない• 速い!
– クラスを使ったイテレータより 100% 以上速いケースも
– ほとんどローカル変数のみで実行できる– 状態を退避する必要がない
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
Generator 版 AlterIter
def AlterIter(seq1, seq2): _max = min(len(seq1), len(seq2)) for idx in range(_max): yield seq1[idx] yield seq2[idx]
• イテレータクラスより簡単• 高速!
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
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 ...
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
イテレータの合成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,
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
パフォーマンス比較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))
test1() test2() test3()
0.72秒 0.59秒 1.08秒
結果測定環境:Python2.3.3Windows2000
•イテレータの二段重ねでもそれほど遅くはならない
•柔軟性とのトレードオフ
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
参考リンク• PEP 234 Iterators
http://www.python.org/peps/pep-0234.html
• PEP 255 Simple Generatorshttp://www.python.org/peps/pep-0255.html
Lightweight Language Weekend ls-lRシェル
Copyrightc2004Python JapanUser'sGroup.
終わりに• イテレータを使おう。イテレー
タは Pythonicだ。• Generator を使おう。 Generator
は魔法じゃない。