今回は、SimplePrograms の 20lines プログラムの続きで、yield 関連について見てみます。
その前に、プログラミング言語学習で役立つと思っているチートシート (Cheat Sheet) を探してみました。
Python 3 Cheat Sheet
https://perso.limsi.fr/pointal/_media/python:cours:mementopython3-english.pdf
チートシートは、昔はC言語とかに対してコンピュータメーカーが紙で作って配っていたりしてたようですが、今ではインターネットから得られるので Python3 関連も用意されています。チートシートで重要なのは印刷したときに一枚の紙に収まることだと思いますが、現在のプリンタではA4 用紙の両面印刷で収まるかどうかで判断してこれをご紹介しました。
さて、yield 文に戻りますが、
https://docs.python.org/ja/3/reference/simple_stmts.html#the-yield-statement
yield 文は意味的に yield 式 と同じです。yield 文を用いると yield 式文で必要な括弧を省略することが出来ます。
yield 式及び文は generator を定義するときに、その本体内でのみ使うことが出来ます。関数定義内で yield を使用することで、その定義は通常の関数でなくジェネレータ関数になります。
なので、具体的な説明は yield式に書いてあるそうです。
https://docs.python.org/ja/3/reference/expressions.html#yield-expressions
関数の本体で yield 式 を使用するとその関数はジェネレータ関数になり、
であり、ジェネレータ関数の説明は、
ジェネレータ関数が呼び出された時、ジェネレータとしてのイテレータを返します。ジェネレータはその後ジェネレータ関数の実行を制御します。ジェネレータのメソッドが呼び出されると実行が開始されます。開始されると、最初の yield 式まで処理して一時停止し、呼び出し元へ expression_list の値を、または expression_list が省略されていれば None を返します。ここで言う一時停止とは、ローカル変数の束縛、命令ポインタや内部の評価スタック、そして例外処理のを含むすべてのローカル状態が保持されることを意味します。再度、ジェネレータのメソッドが呼び出されて実行を再開した時、ジェネレータは yield 式がただの外部呼び出しであったかのように処理を継続します。再開後の yield 式の値は実行を再開するメソッドに依存します。__next__() を使用した場合 (一般に for 文や組み込み関数 next() など) の結果は None となり、send() を使用した場合はそのメソッドに渡された値が結果になります。
なので、yield文による動きも前回通りですね。最後の方にある next()の記述が気になったので試してみると、
>>> a = iter_primes()
>>> a
<generator object iter_primes at 0xf72a7db8>
>>> b = next(a)
>>> print(b)
2
>>> b = next(a)
>>> print(b)
3
>>> b = next(a)
>>> print(b)
5
>>> b = next(a)
>>> print(b)
7
>>>
という感じで、next()は問題なく使えるようです。これは、
https://docs.python.org/ja/3/glossary.html#term-generator
(ジェネレータ) generator iterator を返す関数です。 通常の関数に似ていますが、 yield 式を持つ点で異なります。 yield 式は、 for ループで使用できたり、next() 関数で値を 1 つずつ取り出したりできる、値の並びを生成するのに使用されます。
(ジェネレータイテレータ) generator 関数で生成されるオブジェクトです。
で説明されていました。同様に glossary から、
iterable(イテラブル)が、
メンバーを一度に 1 つずつ返すことができるオブジェクト。
iterator(イテレータ)が、
データの流れを表現するオブジェクトです。イテレータの __next__() メソッドを繰り返し呼び出す (または組み込み関数 next() に渡す) と、流れの中の要素を一つずつ返します。データがなくなると、代わりに StopIteration 例外を送出します。
と、なっていました。
今回の 20lines のプログラムでは、
関数 iter_primes() 定義内で、yield 文が使われているので、その関数は「ジェネレータ関数」になり、「ジェネレータ関数が呼び出された時、ジェネレータとしてのイテレータを返」すので、そのオブジェクトを変数に代入して、next()とかで要素をひとつづつ取り出せるようになり、
それをfor 文で1000まで出力するわけですね。
さらに、組み込み関数filter() ですが、
https://docs.python.org/ja/3/library/functions.html#filter
filter(function, iterable)
iterable の要素のうち、 function が真であるものからイテレータを構築します。 iterable にはシーケンスか、イテレーションをサポートするコンテナか、イテレータを渡せます。 function が None のときは恒等関数が指定されたものとして扱われ、 iterable のうち偽であるものがすべて取り除かれます。
となるので、
filter(prime.__rmod__, numbers)
は、イテレータ numbers から、prime で割り切れる(0なので偽になる)ものを除いて、新たなイテレータを構築するわけですね。
yield も filter() も面白い動きをしますね。これが、SimplePrograms に出てきたということで、入門時に是非知っておいてほしい Python の一押しの機能ということかもしれません。
その前に、プログラミング言語学習で役立つと思っているチートシート (Cheat Sheet) を探してみました。
Python 3 Cheat Sheet
https://perso.limsi.fr/pointal/_media/python:cours:mementopython3-english.pdf
チートシートは、昔はC言語とかに対してコンピュータメーカーが紙で作って配っていたりしてたようですが、今ではインターネットから得られるので Python3 関連も用意されています。チートシートで重要なのは印刷したときに一枚の紙に収まることだと思いますが、現在のプリンタではA4 用紙の両面印刷で収まるかどうかで判断してこれをご紹介しました。
さて、yield 文に戻りますが、
https://docs.python.org/ja/3/reference/simple_stmts.html#the-yield-statement
yield 文は意味的に yield 式 と同じです。yield 文を用いると yield 式文で必要な括弧を省略することが出来ます。
yield 式及び文は generator を定義するときに、その本体内でのみ使うことが出来ます。関数定義内で yield を使用することで、その定義は通常の関数でなくジェネレータ関数になります。
なので、具体的な説明は yield式に書いてあるそうです。
https://docs.python.org/ja/3/reference/expressions.html#yield-expressions
関数の本体で yield 式 を使用するとその関数はジェネレータ関数になり、
であり、ジェネレータ関数の説明は、
ジェネレータ関数が呼び出された時、ジェネレータとしてのイテレータを返します。ジェネレータはその後ジェネレータ関数の実行を制御します。ジェネレータのメソッドが呼び出されると実行が開始されます。開始されると、最初の yield 式まで処理して一時停止し、呼び出し元へ expression_list の値を、または expression_list が省略されていれば None を返します。ここで言う一時停止とは、ローカル変数の束縛、命令ポインタや内部の評価スタック、そして例外処理のを含むすべてのローカル状態が保持されることを意味します。再度、ジェネレータのメソッドが呼び出されて実行を再開した時、ジェネレータは yield 式がただの外部呼び出しであったかのように処理を継続します。再開後の yield 式の値は実行を再開するメソッドに依存します。__next__() を使用した場合 (一般に for 文や組み込み関数 next() など) の結果は None となり、send() を使用した場合はそのメソッドに渡された値が結果になります。
なので、yield文による動きも前回通りですね。最後の方にある next()の記述が気になったので試してみると、
>>> a = iter_primes()
>>> a
<generator object iter_primes at 0xf72a7db8>
>>> b = next(a)
>>> print(b)
2
>>> b = next(a)
>>> print(b)
3
>>> b = next(a)
>>> print(b)
5
>>> b = next(a)
>>> print(b)
7
>>>
という感じで、next()は問題なく使えるようです。これは、
https://docs.python.org/ja/3/glossary.html#term-generator
(ジェネレータ) generator iterator を返す関数です。 通常の関数に似ていますが、 yield 式を持つ点で異なります。 yield 式は、 for ループで使用できたり、next() 関数で値を 1 つずつ取り出したりできる、値の並びを生成するのに使用されます。
(ジェネレータイテレータ) generator 関数で生成されるオブジェクトです。
で説明されていました。同様に glossary から、
iterable(イテラブル)が、
メンバーを一度に 1 つずつ返すことができるオブジェクト。
iterator(イテレータ)が、
データの流れを表現するオブジェクトです。イテレータの __next__() メソッドを繰り返し呼び出す (または組み込み関数 next() に渡す) と、流れの中の要素を一つずつ返します。データがなくなると、代わりに StopIteration 例外を送出します。
と、なっていました。
今回の 20lines のプログラムでは、
関数 iter_primes() 定義内で、yield 文が使われているので、その関数は「ジェネレータ関数」になり、「ジェネレータ関数が呼び出された時、ジェネレータとしてのイテレータを返」すので、そのオブジェクトを変数に代入して、next()とかで要素をひとつづつ取り出せるようになり、
それをfor 文で1000まで出力するわけですね。
さらに、組み込み関数filter() ですが、
https://docs.python.org/ja/3/library/functions.html#filter
filter(function, iterable)
iterable の要素のうち、 function が真であるものからイテレータを構築します。 iterable にはシーケンスか、イテレーションをサポートするコンテナか、イテレータを渡せます。 function が None のときは恒等関数が指定されたものとして扱われ、 iterable のうち偽であるものがすべて取り除かれます。
となるので、
filter(prime.__rmod__, numbers)
は、イテレータ numbers から、prime で割り切れる(0なので偽になる)ものを除いて、新たなイテレータを構築するわけですね。
yield も filter() も面白い動きをしますね。これが、SimplePrograms に出てきたということで、入門時に是非知っておいてほしい Python の一押しの機能ということかもしれません。