質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

2608閲覧

ジェネレータ式の仕組み

suuuu

総合スコア15

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2018/09/23 07:07

編集2018/09/23 07:41

解決したいこと

Pythonの勉強をしています。
ジェネレータ式の仕組みに関して、ご教示お願い致します。

前提として、私はジェネレータをジェネレータたらしめているものはyieldだと認識しています。
根拠は、以下の内容からです。

returnで関数を定義した場合、その関数をコールすると

<function test at xxxxxxxxxxxxxx>

のような値が返り、functionだということがわかります。

しかし、返り値をyieldで返すようにして定義すると、同じようにコールした場合、

<generator object test at xxxxxxxxxxxxxx>

と値が返り、generatorであることがわかります。

以上の理由で、yieldがジェネレータをジェネレータたらしめていると認識しているのですが、
そうなるとジェネレータ式に関して疑問が生じます。

例えば、以下のようにしてジェネレータ式が書けるかと思いますが、yieldがありません。

>>> test = (i for i in range(0,10)) >>> test <generator object <genexpr> at xxxxxxxxxxxxxx>

ジェネレータ式では何を以てジェネレータとして生成されているのでしょうか。
裏でyieldが自動定義されているのでしょうか。
詳しい方、ご教示いただけると嬉しいです。

補足情報(FW/ツールのバージョンなど)

Python 3.6.5

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

pythonにおいてジェネレータに関連する用語としては、

  • ジェネレータ関数
  • ジェネレータ式
  • ジェネレータイテレータ

の3つがあります。

結論としては、ジェネレータ関数とジェネレータ式はともにジェネレータイテレータを生成します。動作としてはそういうもの、で説明終わりです。

ただ、ジェネレータ式についてはドキュメントを読んだ限りでは位置づけがよくわからないというのが私の率直な感想でした。

ジェネレータの定義

(ジェネレータ) generator iterator を返す関数です。 通常の関数に似ていますが、 yield 式を持つ点で異なります。 yield 式は for ループで使用出来る一連の値や、next() 関数で一度に 1 つの値を取り出すのに使用されます。

用語集 — Python 3.5.3 ドキュメント ジェネレータ

 

Python における generator (ジェネレータ) は、イテレータプロトコルを実装する便利な方法を提供します。コンテナオブジェクトの iter() メソッドがジェネレータとして実装されていれば、そのメソッドは iter() および next() メソッドを提供するイテレータオブジェクト (厳密にはジェネレータオブジェクト) を自動的に返します。ジェネレータに関する詳細な情報は、 yield 式のドキュメント にあります。

4. 組み込み型 — Python 3.5.3 ドキュメント 4.5.1. ジェネレータ型

ジェネレータ式の定義

ジェネレータ式 (generator expression) とは、丸括弧を使ったコンパクトなジェネレータ表記法です

6. 式 (expression) — Python 3.5.3 ドキュメント 6.2.8. ジェネレータ式

ジェネレータはyield式によって実現されるもの、という説明しかないので、ジェネレータ式が思いっきり浮いてるんですよね・・・。

ドキュメントですらそういう説明になっている以上、質問の疑問はごく真っ当なものだと思います。

仕方ないのでPEPを見に行くと、

The semantics of a generator expression are equivalent to creating an anonymous generator function and calling it.

PEP 289 -- Generator Expressions | Python.org

無名のジェネレータ関数を作って呼び出すことと等価、とあり、とりあえず安心はできました。

そうか、無名関数なのか。見た目は内包表記だけど、本当はlambdaの仲間だったんですねぇ(?)。

ただ、「裏でyieldが自動定義されているのでしょうか。」という疑問に直接答えるのは難しくて、というか処理系の実装を読んでみないとわからない領域だと思います。そもそもジェネレータがどんな内部表現で実現されているかという話なので。

「過程はともかく、結果としてジェネレータ関数で定義した場合と同様に振る舞う」くらいに思っておいた方が平和的です・・・。


参考情報:
Python のイテレータ生成クラスの使い方 - Life with Python
6. 式 (expression) — Python 3.6.5 ドキュメント | 6.2.8. ジェネレータ式
Pythonのイテレータとジェネレータ
Pythonのイテレータ、ジェネレータまわりの言語仕様について復習してみる
Python: ジェネレータをイテレータから理解する - CUBE SUGAR CONTAINER

投稿2018/09/23 07:47

編集2018/09/23 11:26
hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

suuuu

2018/09/23 08:08 編集

回答ありがとうございます。 __iter__()と__next__()という2つのメソッドはイテレータの要素と認識していましたが違うのでしょうか。 ジェネレータはイテレータを生成するための道具と思っており、iter()関数とジェネレータで生成したイテレータは違うものと認識しております。 その辺の認識もちょっと曖昧なので、参考であげていただいたページを拝見させていただきます。
hayataka2049

2018/09/23 08:16

あれ、たしかにそのとおりかもしれません。 https://docs.python.jp/3.5/glossary.html#term-generator によると、 yieldを使って定義された関数そのもの:ジェネレータ関数 その関数が生成するもの:ジェネレータイテレータ ジェネレータ式:イテレータを返す という説明になっています。
hayataka2049

2018/09/23 08:20

ちょっと自己弁護しておくと、wikipediaなんかによればジェネレータは一般的には「値を次々と取り出す。取り出すときに動的に値を生成する」みたいなものとされ、この文脈で説明するならイテレータはジェネレータの一種(最も単純な形)ということになります。 私の回答もそのつもりで書いていたのですが、python的には間違いかもしれません。
suuuu

2018/09/23 08:23

iter()は、リスト等のコンテナを渡すと、イテレータオブジェクトを生成してくれますが、 渡したコンテナの要素にnext()でアクセスすることができるのみという認識でした。 一方で、ジェネレータは、ループ処理の中身をある程度自由にプログラミングしてyieldで返せるので、単にiter()関数を使うよりも自由にイテレータを生成できるなと感じておりました。
suuuu

2018/09/23 08:24

いやあ、難しいですね!
hayataka2049

2018/09/23 08:27

iter()はオブジェクトの__iter__()を呼び出すんですね(それだけする訳ではないけど)。next()も大体同様。ということで、自作クラスを作って__iter__()と__next__()を書けばyieldと同等の自由度が確保されます。面倒だけど。
LouiS0616

2018/09/23 08:29

Python的には、ジェネレータはイテレータの一種です。 ジェネレータ関数及びジェネレータ式によって、ジェネレータイテレータ(あるいは単にジェネレータとも呼ぶ)が生成されます。 私もよく用語の用法で混乱するので、そのようなときはこのページを参考にしています。 英語なので読むのは大変ですが、図を眺めるだけでもなかなか面白いですよ。 https://nvie.com/posts/iterators-vs-generators/ 横から失礼しました。
hayataka2049

2018/09/23 08:32 編集

>LouiS0616さん ありがとうございます やっぱりpythonではイテレータが普遍的な概念なのですね・・・ジェネレータ(イテレータ)はその一種で、具体的には「ジェネレータ関数とジェネレータ式によって生成されるものの名前」か(すごくトートロジーっぽいですが) 納得したので回答を後で直しておきます
suuuu

2018/09/23 08:40

御二方ともありがとうございます。 >LouiS0616さん ご紹介いただいたページも拝読させていただきます。
suuuu

2018/09/24 01:11

>hayataka2049さん 回答の編集を拝読致しました。 ありがとうございます。 >「過程はともかく、結果としてジェネレータ関数で定義した場合と同様に振る舞う」 PEP289の内容も参照させていただき、その結論、承知致しました。 いずれは処理の実装部分というのも確認して納得のいく結論に達したいと思いますが… まだ学習を始めたばかりということもあり、今は先に進みたいと思います。 まあ、納得のいく結論に達したら、ここにでも報告します。 貴重な時間をありがとうございました。 teratailで初めての質問だったのですが、これほど親身に対応していただけたことに感動しました。
guest

0

ベストアンサー

リスト内包表記や辞書内包表記と同じで、単に簡潔に書くことができるだけです。
例えば次の二つのコードは同等です。

Python

1def make_gen(): 2 for B in C: 3 yield A(B) 4 5gen = make_gen()

Python

1gen = (A(B) for B in C)

書き方という点では理解しましたが、なぜそれでジェネレータとして認識されるのかが気になっています。

構文がどう解析されているか、という話でしょうか。
それならば言語リファレンスを見てみてはいかがでしょう。

generator_expression ::= "(" expression comp_for ")"

comp_for ::= [ASYNC] "for" target_list "in" or_test [comp_iter]

引用元:

真面目に読むのはかなり骨なのでここでは避けますが、
なんとなく(... for ... in ...)の形が解析上見られていることは読み取れます。

投稿2018/09/23 07:18

編集2018/09/23 07:47
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

suuuu

2018/09/23 07:40

回答ありがとうございます。 簡潔に書くことができるというのは認識しているのですが、お尋ねしたいのはジェネレータ式がジェネレータとして認識される仕組みです。 言葉足らずな質問で申し訳ありません。 例えば、 >>> test = ([i for i in range(0,10)]) >>> test [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] というリスト内包があるとしますが、これをジェネレーター式で表す場合、 >>> test = (i for i in range(0,10)) と書けるかと思います。 両者の違いは[]があるかどうかです。 書き方という点では理解しましたが、なぜそれでジェネレータとして認識されるのかが気になっています。
LouiS0616

2018/09/23 07:42

リスト内包には丸括弧はいらないです。 test = [i for i in range(0,10)] で充分。 この場合のカッコは単に計算順序を制御する意味で解釈されます。 --- ジェネレータ式の場合、丸括弧自体が構文上意味を持ちます。
suuuu

2018/09/23 07:55

リスト内包に丸括弧が要らない件、承知しました。すみません。 回答の編集頂いた内容を読みました。 >構文がどう解析されているか、という話でしょうか。 >それならば言語リファレンスを見てみてはいかがでしょう。 学習を始めたばかりなもので、恥ずかしながら言語リファレンスの存在を知りませんでした。 知りたかった答えはここにありそうなので、じっくり読んでみます。 回答ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問