Table of Contents
Generator
Def: Generator
- ジェネレーターオブジェクトとは, Pythonのシーケンスを作成するオブジェクトのこと.
- ジェネレーターを用いることで, シーケンス全体を作ってメモリに格納しなくても, シーケンスを反復処理することができる
- ジェネレーターは反復処理のたびに最後に呼び出されたときにどこにいたかを管理し, 次の値を返す
Python 3.xではrange
はジェネレーターの一つですが, Python 2.xではrange
はリストを返す挙動でした.
そのため, リスト形式でメモリに収まる範囲内の整数のシーケンスしか扱うことができないというデメリットがありました.
ジェネレーターの例
平方数を返すジェネレーターを以下のように定義してみます.
1
2
3
4
5
def squared_generator(START: int, LIMIT: int):
num = START
while num < LIMIT:
yield num**2
num += 1
このとき, squared_generator
は以下のようにジェネレーターオブジェクトを返します
1
2
print(squared_generator(1, 5))
>>> <generator object squared_generator at 0x7f4cfc53fd30>
反復処理は以下のようにして実行可能です
1
2
3
4
5
6
7
for x in squared_generator(1, 5):
print(x)
>>> 1
>>> 4
>>> 9
>>> 16
ジェネレーターはいつも早いのか?
メモリ効率的ではありますが, 実行速度は必ずしも早いとは限りません.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def compute_by_generator(N):
start_map = time.time()
res = sum(squared_generator(0, N))
print(time.time() - start_map, res)
def compute_by_list(N):
squared_list = []
for i in range(0, N):
squared_list.append(i**2)
start_map = time.time()
res = sum(squared_list)
print(time.time() - start_map, res)
N = 10000000
compute_by_list(N)
>>> 0.1008293628692627 333333283333335000000
compute_by_generator(N)
>>> 0.4377624988555908 333333283333335000000
Generatorの値をfilterする
filter
関数
Def:
filter
関数は第1引数に指定した関数を用いて, 第2引数に指定した反復可能オブジェクトの要素を評価し,
その結果が真となる要素だけを反復するイテレータを返す機能を持つ.
1
filter(function, iterable)
なお, function
とiterable
について以下のような制約がある
- 受け取る反復可能オブジェクトは1つだけ
function
に指定する関数の引数は1つだけ
例として, 与えられたリストから偶数の要素のみを抽出したい場合は
1
2
3
4
5
6
7
8
9
def is_even(x):
return x % 2 == 0
result = filter(is_even, [1, 2, 3, 4])
print(result)
>>> <filter object at 0x7f1b70294ac0>
print(list(result))
>>> [2, 4]
Generator objectに対するfilter
0から始まる平方数を返すgeneratorを以下のように定義します
1
2
3
4
5
def squared_generator():
base = 0
while True:
yield(base**2)
base += 1
このとき, 先頭の3個の平方数を出力したい場合は
1
2
3
4
5
6
for i in range(0, 3):
print(i, next(A))
>>> 0 0
1 1
2 4
1000以上の平方数の中から先頭の3個の平方数を出力したい場合は
1
2
3
4
5
6
7
8
squaered_over_thousand = filter(lambda x: x >= 1000, squared_generator())
for i in range(0, 3):
print(i, next(squaered_over_thousand))
>>> 0 1024
1 1089
2 1156
REMARKS
Generatorの定義にもよりますが, 上記のようにwhile True
で定義されたGeneratorに対して
1
list(filter(lambda x: x >= 1000, squared_generator()))
を実行してしまうと, すべての要素(実質無限)に対して真偽判定してからlist
objectを返そうとしてしまうので,
いつまで経っても実行結果が返ってこないという悲惨な結果が起こってしまいます.
(注意:GitHub Accountが必要となります)