はじめに
このセクションでは、ジェネレータ式や itertools モジュールなど、ジェネレータに関連する追加のトピックを紹介します。
ジェネレータ式
リスト内包表記のジェネレータ版です。
>>> a = [1,2,3,4]
>>> b = (2*x for x in a)
>>> b
<generator object at 0x58760>
>>> for i in b:
... print(i, end=' ')
...
2 4 6 8
>>>
リスト内包表記との違い
- リストを構築しません。
- 唯一の有用な目的は反復処理です。
- 一度消費すると、再利用できません。
一般的な構文
(<式> for i in s if <条件>)
関数の引数としても使用できます。
sum(x*x for x in a)
任意の反復可能オブジェクトに適用できます。
>>> a = [1,2,3,4]
>>> b = (x*x for x in a)
>>> c = (-x for x in b)
>>> for i in c:
... print(i, end=' ')
...
-1 -4 -9 -16
>>>
ジェネレータ式の主な用途は、シーケンスに対して何らかの計算を行い、結果を一度だけ使用するコードにあります。たとえば、ファイルからすべてのコメントを取り除きます。
f = open('somefile.txt')
lines = (line for line in f if not line.startswith('#'))
for line in lines:
...
f.close()
ジェネレータを使用すると、コードが高速に実行され、メモリを少なく使用します。ストリームに適用されるフィルタのようなものです。
ジェネレータの理由
- 多くの問題は、反復処理の観点からはるかに明確に表現できます。
- アイテムのコレクションをループ処理し、ある種の操作(検索、置換、変更など)を行う。
- 処理パイプラインは、幅広いデータ処理問題に適用できる。
- メモリ効率が良い。
- 必要なときにのみ値を生成する。
- 巨大なリストを構築することとの対照。
- ストリーミングデータで動作できる
- ジェネレータはコードの再利用を促進する
- 反復処理を、反復処理を使用するコードから分離する。
- 興味深い反復処理関数のツールボックスを作成し、「ミックスアンドマッチ」できる。
itertools モジュール
itertools は、反復子/ジェネレータを助けるために設計されたさまざまな関数を備えたライブラリモジュールです。
itertools.chain(s1,s2)
itertools.count(n)
itertools.cycle(s)
itertools.dropwhile(predicate, s)
itertools.groupby(s)
itertools.ifilter(predicate, s)
itertools.imap(function, s1,... sN)
itertools.repeat(s, n)
itertools.tee(s, ncopies)
itertools.izip(s1,..., sN)
すべての関数は、データを反復的に処理します。さまざまな種類の反復パターンを実装しています。
PyCon '08 の Generator Tricks for Systems Programmers チュートリアルで詳細を確認できます。
前のエクササイズでは、ログファイルに書き込まれる行に追従し、それらを行のシーケンスに解析するコードをいくつか書きました。このエクササイズはそれに基づいてさらに構築されます。stocksim.py がまだ実行されていることを確認してください。
演習 6.13: ジェネレータ式
ジェネレータ式は、リスト内包表記のジェネレータ版です。たとえば:
>>> nums = [1, 2, 3, 4, 5]
>>> squares = (x*x for x in nums)
>>> squares
<generator object <genexpr> at 0x109207e60>
>>> for n in squares:
... print(n)
...
1
4
9
16
25
リスト内包表記とは異なり、ジェネレータ式は一度だけ使用できます。したがって、別の for ループを試すと、何も得られません:
>>> for n in squares:
... print(n)
...
>>>
演習 6.14: 関数の引数におけるジェネレータ式
ジェネレータ式は、時々関数の引数に置かれます。最初は少し奇妙に見えますが、この実験を試してみてください:
>>> nums = [1,2,3,4,5]
>>> sum([x*x for x in nums]) ## リスト内包表記
55
>>> sum(x*x for x in nums) ## ジェネレータ式
55
>>>
上記の例では、大きなリストを操作している場合、ジェネレータを使用した 2 番目のバージョンがはるかに少ないメモリを使用します。
あなたの portfolio.py ファイルでは、リスト内包表記を含むいくつかの計算を行いました。これらをジェネレータ式に置き換えてみてください。
演習 6.15: コードの簡略化
ジェネレータ式は、小さなジェネレータ関数の便利な代替手段となることがよくあります。たとえば、次のような関数を書く代わりに:
def filter_symbols(rows, names):
for row in rows:
if row['name'] in names:
yield row
次のようなコードを書くことができます:
rows = (row for row in rows if row['name'] in names)
ticker.py プログラムを修正して、適切にジェネレータ式を使用するようにします。
まとめ
おめでとうございます!あなたは More Generators の実験を完了しました。あなたのスキルを向上させるために、LabEx でさらに実験を行って練習してください。