1: 2017-05-20 (土) 06:18:07 njf |
現: 2017-05-20 (土) 15:37:34 njf |
| + | *Pythonのyieldとは [#t31c98e6] |
| yieldはジェネレーター関数を作るための関数です。 | | yieldはジェネレーター関数を作るための関数です。 |
| | | |
- | ジェネレーター関数はイテレーターを簡単にするための関数で、イテレーターとは繰り返し処理のことです。 | + | ジェネレーター関数はイテレーターを簡単に作成するための関数で、イテレーターとは繰り返し処理のことです。 |
| | | |
| 特にPythonでは、ジェネレーター関数を使うとforループなどを効率よく書けるようになります。 | | 特にPythonでは、ジェネレーター関数を使うとforループなどを効率よく書けるようになります。 |
| 例えば、 | | 例えば、 |
| | | |
- | def yieldTest(n): | + | def yieldTest(): |
| yield 1 | | yield 1 |
| yield 2 | | yield 2 |
| yield 3 | | yield 3 |
- | | + | |
- | for i in yieldTest(10): | + | for i in yieldTest(): |
| print i | | print i |
| | | |
| | | |
| となり、yieldで設定した値がループで使えるようになっています。 | | となり、yieldで設定した値がループで使えるようになっています。 |
| + | |
| + | ループではなく、「next」という関数で一つずつ値を取り出すことも出来ます。 |
| + | |
| + | yt = yieldTest() |
| + | |
| + | print next(yt) |
| + | print next(yt) |
| + | print next(yt) |
| + | print next(yt) |
| + | |
| + | 結果: |
| + | 1 |
| + | 2 |
| + | 3 |
| + | Traceback (most recent call last): |
| + | File "yieldtest.py", line 19, in <module> |
| + | print next(yt) |
| + | StopIteration |
| + | |
| + | 値が無くなると例外が発生します。forループではnextを使って例外が発生するまで値を取り出している、と考えて良いでしょう。 |
| + | |
| + | このとき、注意すべきなのは、ジェネレーター関数は呼び出されたときに全ての処理を行うわけではないことです。つまり。nextが呼ばれると、yieldの場所で処理が止まり、次のnextで再びその場所から処理が始まります。 |
| + | |
| + | 例えば次のように関数を変更した場合、処理が一回ずつ止まっていることがわかります。 |
| + | |
| + | def yieldTest(): |
| + | print "yield 1" |
| + | yield 1 |
| + | print "yield 2" |
| + | yield 2 |
| + | print "yield 3" |
| + | yield 3 |
| + | |
| + | yt = yieldTest() |
| + | |
| + | print next(yt) |
| + | print next(yt) |
| + | print next(yt) |
| + | |
| + | 結果: |
| + | yield 1 |
| + | 1 |
| + | yield 2 |
| + | 2 |
| + | yield 3 |
| + | 3 |
| + | |
| + | 最初に「yieldTest」を呼び出したときに全ての処理が行われているわけではないことがわかります。 |
| + | |
| + | そのため、ジェネレーター関数に無限ループがあったとしても問題ありません。 |
| + | |
| + | def yieldTest(): |
| + | i = 0 |
| + | while True: |
| + | i += 1 |
| + | yield i |
| + | |
| + | yt = yieldTest() |
| + | |
| + | print next(yt) |
| + | print next(yt) |
| + | print next(yt) |
| + | |
| + | 結果: |
| + | 1 |
| + | 2 |
| + | 3 |
| + | |
| + | もしyieldTestを呼び出したときに処理がまとめて行われるのであれば、無限ループのせいでこのコードは実行時に止まってしまいますが、そうはなりません。nextが呼び出されるたびに、次のyieldが呼び出されるまで処理が行われるためです。 |
| + | |
| + | 実際にジェネレーター関数を使う時には、このようにループの中にyieldを書くことが多いでしょう。 |
| + | |
| + | *yieldの使いどころ [#r16eb4ac] |
| + | |
| + | 単純なループなら特にyieldを使う意味は感じられないかも知れません。 |
| + | |
| + | しかし、複雑なループを何度も使う時にはyieldが威力を発揮します。 |
| + | |
| + | 例えば可変長のデータが入ったファイルを読み込んで1データずつ処理したいとします。この場合には、読み込んだデータに応じて次のデータを返していく関数をyieldで定義しておけば、処理が簡単になり再利用もしやすくなります。 |
| + | |
| + | 他にも特定の場所を表すようなデータをある条件の満たした経路の順で処理をしたい場合など、ループの条件が複雑で、しかも何度もそれを使わないといけない、という時には、いちいちループの中で処理を書くよりもジェネレーター関数を作っておく方が便利です。 |