2019年10月27日

空のジェネレータの作り方

Python を書いていると、空のジェネレータが必要となる時が時々あります。これからジェネレータを実装するが、ひとまず空にしておく、というようなケースですね。
いざ空のジェネレータを書こうとすると、専用文法は用意されていないことに気づきます。

空の関数を作ってみると、、、

>>> def void():
...     pass
...
>>> type(void())
<class 'NoneType'>  # NG
>>> list(void())
Traceback (most recent call last):  # NG
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not iterable
yield キーワードが無いと、そもそもジェネレータになりません。



続いて、yield だけを呼んでみると、、、

>>> def void():
...     yield
...
>>> type(void())
<class 'generator'>  # OK
>>> list(void())
[None]  # NG
yield に到達するのでサイズが1となってしまいます。



yield を使いつつ、yield に到達させないことで、空のジェネレータを作ることができます。

>>> def void():
...     if False:
...         yield  # yieldが存在するが到達しない
...
>>> type(void())
<class 'generator'>  # OK
>>> list(void())
[]  # OK



別の書き方として、yield 到達前に return させるやり方もあります。

>>> def void():
...     return
...     yield  # yieldが存在するが到達しない
...
>>> type(void())
<class 'generator'>  # OK
>>> list(void())
[]  # OK



おそらく最も華麗な書き方は、yield from を使うやり方です。

>>> def void():
...     yield from ()  # 空tupleから空ジェネレータを作成
...
>>> type(void())
<class 'generator'>  # OK
>>> list(void())
[]  # OK
1行で書けている点がいいですね。ただし、yield from は Python 3.3 からの機能となります。



変化球として、ジェネレータを返す関数を定義するやり方もあります。

>>> def void():
...     return (_ for _ in ())  # この関数はジェネレータをreturnする
...
>>> type(void())
<class 'generator'>  # OK
>>> list(void())
[]  # OK



色々試すと、ちょっとした頭の体操になりますね。

0 件のコメント:

コメントを投稿