130

Pythonでは、次のように関数の本体にyieldキーワードを配置することで、イテレータ関数を簡単に定義できます。

def gen():
    for i in range(100):
        yield i

値を生成しない(0の値を生成する)ジェネレーター関数を定義するにはどうすればよいですか?Pythonはジェネレーターであり、通常の関数ではないことを認識できないため、次のコードは機能しません。

def empty():
    pass

私は次のようなことをすることができます

def empty():
    if False:
        yield None

しかし、それは非常に醜いでしょう。空のイテレータ関数を実現するための良い方法はありますか?

4

9 に答える 9

167

returnジェネレーターで1回使用できます。何も生成せずに反復を停止するため、関数をスコープ外に実行させるための明示的な代替手段を提供します。したがって、を使用yieldして関数をジェネレーターに変換しますが、その前にreturnジェネレーターを終了してから何かを生成します。

>>> def f():
...     return
...     yield
... 
>>> list(f())
[]

あなたが持っているものよりもはるかに優れているかどうかはわかりません-それは単にno-opifステートメントをno-opステートメントに置き換えるだけyieldです。しかし、それはもっと慣用的です。yield使用するだけでは機能しないことに注意してください。

>>> def f():
...     yield
... 
>>> list(f())
[None]

使ってみませんiter(())か?

この質問は、特に空のジェネレーター関数について尋ねます。そのため、一般的に空のイテレータを作成するための最良の方法についての質問ではなく、Pythonの構文の内部整合性についての質問と見なします。

質問が実際に空のイテレータを作成するための最良の方法に関するものである場合は、代わりに使用することについてZectbumoiter(())に同意するかもしれません。iter(())ただし、関数を返さないことに注意することが重要です。空のイテラブルを直接返します。通常のジェネレーター関数のように、呼び出されるたびに反復可能オブジェクトを返す呼び出し可能オブジェクトを期待するAPIを使用しているとします。次のようなことをする必要があります。

def empty():
    return iter(())

(クレジットは、この回答の最初の正しいバージョンを提供するためにUnutbuに行く必要があります。)

さて、あなたは上記をより明確に感じるかもしれませんが、私はそれがより明確でない状況を想像することができます。(考案された)ジェネレーター関数定義の長いリストのこの例を考えてみましょう。

def zeros():
    while True:
        yield 0

def ones():
    while True:
        yield 1

...

その長いリストの最後にyield、次のようなが含まれているものが表示されます。

def empty():
    return
    yield

または、Python 3.3以降(DSMで提案されている)では、次のようになります。

def empty():
    yield from ()

キーワードの存在は、yieldこれが他のすべてとまったく同じように、単なる別のジェネレーター関数であることを一目で明らかにします。iter(())バージョンが同じことをしていることを確認するには、もう少し時間がかかります。

yield微妙な違いですが、正直なところ、ベースの関数の方が読みやすく、保守しやすいと思います。

このアプローチが望ましい別の理由を示すために使用するuser3840170からのこのすばらしい回答も参照してください。コンパイル時に、発行される命令が最も少なくなります。dis

于 2012-11-06T03:22:44.500 に答える
76
iter(())

ジェネレーターは必要ありません。おいおい!

于 2014-10-09T06:26:25.960 に答える
65

Python 3.3(私がyield fromキックしているため、そして@senderleが私の最初の考えを盗んだため):

>>> def f():
...     yield from ()
... 
>>> list(f())
[]

しかし、私は認めなければなりません。私は、このためのユースケースを思い付くのに苦労していiter([])ます(x)range(0)

于 2012-11-06T03:28:54.490 に答える
21

別のオプションは次のとおりです。

(_ for _ in ())
于 2013-06-25T03:52:37.753 に答える
8

@senderleが言ったように、これを使用してください:

def empty():
    return
    yield

私は主にそれの別の正当化を共有するためにこの答えを書いています。

このソリューションを他のソリューションよりも優先して選択する理由の1つは、通訳者に関する限り、このソリューションが最適であるということです。

>>> import dis
>>> def empty_yield_from():
...     yield from ()
... 
>>> def empty_iter():
...     return iter(())
... 
>>> def empty_return():
...     return
...     yield
...
>>> def noop():
...     pass
...
>>> dis.dis(empty_yield_from)
  2           0 LOAD_CONST               1 (())
              2 GET_YIELD_FROM_ITER
              4 LOAD_CONST               0 (None)
              6 YIELD_FROM
              8 POP_TOP
             10 LOAD_CONST               0 (None)
             12 RETURN_VALUE
>>> dis.dis(empty_iter)
  2           0 LOAD_GLOBAL              0 (iter)
              2 LOAD_CONST               1 (())
              4 CALL_FUNCTION            1
              6 RETURN_VALUE
>>> dis.dis(empty_return)
  2           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
>>> dis.dis(noop)
  2           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE

ご覧のempty_returnとおり、は通常の空関数とまったく同じバイトコードを持っています。残りは、とにかく動作を変更しない他の多くの操作を実行します。empty_returnとの唯一の違いnoopは、前者にはジェネレータフラグが設定されていることです。

>>> dis.show_code(noop)
Name:              noop
Filename:          <stdin>
Argument count:    0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals:  0
Stack size:        1
Flags:             OPTIMIZED, NEWLOCALS, NOFREE
Constants:
   0: None
>>> dis.show_code(empty_return)
Name:              empty_return
Filename:          <stdin>
Argument count:    0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals:  0
Stack size:        1
Flags:             OPTIMIZED, NEWLOCALS, GENERATOR, NOFREE
Constants:
   0: None

もちろん、この議論の強さは、使用中のPythonの特定の実装に大きく依存します。十分に賢い代替インタプリタは、他の操作が何の役にも立たないことに気づき、それらを最適化するかもしれません。ただし、そのような最適化が存在する場合でも、インタプリタはそれらの実行に時間を費やし、iterグローバルスコープの識別子が他の何かにリバウンドされるなど、最適化の仮定が破られるのを防ぐ必要があります(それがバグを示している可能性が高い場合でも)実際に起こった)。最適化するものが何もない場合はempty_return、比較的ナイーブなCPythonでさえ、偽の操作に時間を浪費することはありません。

于 2020-04-29T07:29:31.810 に答える
3

ジェネレーター関数である必要がありますか?そうでない場合はどうですか

def f():
    return iter(())
于 2012-11-06T03:29:00.510 に答える
2

空のイテレータを作成する「標準的な」方法は、iter([])のようです。[]をiter()のデフォルト引数にすることを提案しました。これは適切な議論で拒否されました。http://bugs.python.org/issue25215を参照してください -Jurjen

于 2015-09-22T19:17:47.423 に答える
0
generator = (item for item in [])
于 2015-06-15T20:55:16.833 に答える
0

まだ提案がないので、クラスベースの例を挙げたいと思います。これは、アイテムを生成しない呼び出し可能なイテレータです。これは、問題を解決するための簡単で説明的な方法だと思います。

class EmptyGenerator:
    def __iter__(self):
        return self
    def __next__(self):
        raise StopIteration

>>> list(EmptyGenerator())
[]
于 2020-09-06T06:09:21.610 に答える