Python Cookbook [Creating New Iteration Patterns with Generators : ジェネレーターを利用した独自イテレーションパターンの実装] 投稿一覧へ戻る
Published 2020年5月7日19:19 by mootaro23
SUPPORT UKRAINE
- Your indifference to the act of cruelty can thrive rogue nations like Russia -
Problem:
range() や reversed() といった組み込み関数とは異なるイテレーションパターンを実装したい。
Solution:
generator 関数を利用して実装します。
例えば、ある範囲内、あるステップ数で浮動小数点数を生成したいなら次のようになるでしょう。
def float_range(start, stop, step):
x = start
while x < stop:
yield x
x += step
x = start
while x < stop:
yield x
x += step
こういった関数を利用するには、for ステートメントであるとか、sum() や list() といった内部でイテラブルを利用する他の関数等と一緒に使用します。
for n in float_range(5, 10, 0.5):
print(n)
list(float_range(0, 1, 0.125))
print(n)
# 5
# 5.5
# 6.0
# 6.5
# 7.0
# 7.5
# 8.0
# 8.5
# 9.0
# 9.5
list(float_range(0, 1, 0.125))
# [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875]
関数内に yield ステートメントが含まれていれば、その関数は generator 関数になります。
通常の関数とは異なり、generator 関数はイテレーション操作に反応して機能します。
以下のスニペットは、generator 関数の基本的なメカニズムを理解するためのものです。
python インタラクティブシェルで実行します。
>>> def countdown(n):
... print('{} からカウントダウンを開始します'.format(n))
... while n > 0:
... yield n
... n -= 1
... print('発射!!!!')
>>> c = countdown(3)
>>> c
... print('{} からカウントダウンを開始します'.format(n))
... while n > 0:
... yield n
... n -= 1
... print('発射!!!!')
>>> c = countdown(3)
>>> c
# <generator object countdown at 0x0000000004D34F48>
↑ generator をインスタンス化しただけでは何もプリントアウトされません。
これは、イテレーション操作が一切行われていないためです。
>>> next(c)
3 からカウントダウンを開始します
3
3 からカウントダウンを開始します
3
↑ 最初の yield が実行され値が渡されてきます
>>> next(c)
2
2
↑ 次の yield の実行
>>> next(c)
1
1
↑ その次の yield の実行
>>> next(c)
発射!!!!
Traceback (most recent call last):
File "", line 1, in
StopIteration
発射!!!!
Traceback (most recent call last):
File "
StopIteration
↑ そのまた次の yield の実行。ここで、イテレーション最後だよ、の例外が発生します
ここで分かることは、generator 関数はイテレーション内で実行される "next" 操作に反応して実行される、ということです。
1回 generator 関数が値を返すと、イテレーションはその場所でストップします。
イテレーション操作に通常利用される for ステートメント等では、こういったことが水面下で自動的に行われているわけです。
この記事に興味のある方は次の記事にも関心を持っているようです...
- People who read this article may also be interested in following articles ... -