オブジェクトがPythonのジェネレーターオブジェクトであるかどうかを確認する方法


157

Pythonでは、オブジェクトがジェネレーターオブジェクトかどうかを確認するにはどうすればよいですか?

これを試して-

>>> type(myobject, generator)

エラーを出します-

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'generator' is not defined

(オブジェクトがnextジェネレーターになるためのメソッドがあるかどうかを確認できることはわかっていますが、ジェネレーターだけでなく、オブジェクトのタイプを判別できる方法が欲しいです。)


4
解決しようとしている実際の問題は何ですか?もっとコンテキストを投稿してください。もっと賢い方法があるかもしれません。それがジェネレータかどうかを知る必要があるのはなぜですか?
Daenyth、2011年

7
from types import GeneratorType;type(myobject, GeneratorType)クラス 'generator'のオブジェクトに対して適切な結果が得られます。しかし、デニスが示唆するように、それは必ずしも正しい方法ではありません。
JAB 2011年

7
をチェックしている場合は__next__、ジェネレーターだけでなく、実際には任意のイテレーターを受け入れています。

2
多くの場合、同じコレクションを複数回繰り返すことを望んでいるため、何かがジェネレータであるかどうかを知る本当のポイントは、それらを回避できるようにすることです。
Ian

2
ユースケースについて不思議に思う人にとって、これはイテレータが消費されるかどうかを知る必要がある場合に役立ちます(たとえば、関数がイテレータを受け入れるが、複数回反復する必要がある場合は、反復する前に具体化する必要があります)
wbadart

回答:


227

タイプからGeneratorTypeを使用できます。

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True

5
残念ながら、これはジェネレータクラス(たとえば、マップオブジェクトやフィルタオブジェクト)では機能しません。
Ricardo Cruz

おそらく、isinstance(gen, (types.GeneratorType, map, filter))また、検出するのに有用であるmapfilter。ただし、これには他のイテラブルやイテレータは含まれません。
jlh

38

ジェネレーター関数ですか?使用しますinspect.isgeneratorfunction

編集:

JABのコメントで指摘されているように、generatorオブジェクトが必要な場合は、inspect.isgeneratorを使用できます。


1
ジェネレータ関数はジェネレータオブジェクトではありません。@utdemirの回答を参照してください
Piotr Findeisen

5
@Piotr:その場合はを使用しますinspect.isgenerator
JAB 2011年

@ JAB、@ Piotr:OPの意味のすべての可能性に対処するために反映されています。JABに感謝します:)
mouad

1
注:のみ、このテストが必要な場合は、あなたが使用することにより、小さなオーバーヘッドを回避することができます@utdemirのソリューションをするのでinspect.isgenerator:のみに簡略化したものですisinstance(object, types.GeneratorType)
bufh

ジェネレーターオブジェクトとジェネレーター関数の違いについては、@ RobertLujoの回答を参照してください。stackoverflow.com/a/32380774/3595112
industryworker3595112

24

ジェネレーター関数ジェネレーター(ジェネレーター関数の結果)を区別することが重要だと思います。

>>> def generator_function():
...     yield 1
...     yield 2
...
>>> import inspect
>>> inspect.isgeneratorfunction(generator_function)
True

generator_functionを呼び出すと、通常の結果は得られません。関数自体のコードも実行されません。結果は、generatorと呼ばれる特別なオブジェクトになります。

>>> generator = generator_function()
>>> generator
<generator object generator_function at 0x10b3f2b90>

したがって、それはジェネレータ関数ではなく、ジェネレータです。

>>> inspect.isgeneratorfunction(generator)
False

>>> import types
>>> isinstance(generator, types.GeneratorType)
True

ジェネレータ関数はジェネレータではありません:

>>> isinstance(generator_function, types.GeneratorType)
False

参考までに、関数本体の実際の呼び出しは、ジェネレーターを使用して行われます。例:

>>> list(generator)
[1, 2]

も参照してくださいPythonでは、関数を呼び出す前に「ジェネレーター関数」であるかどうかを確認する方法はありますか?


11

inspect.isgenerator純粋なジェネレーター(つまり、「ジェネレーター」クラスのオブジェクト)をチェックする場合は、この関数で問題ありません。ただしFalse、たとえばizipイテラブルをチェックすると戻ります。一般化されたジェネレータをチェックする別の方法は、この関数を使用することです:

def isgenerator(iterable):
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')

1
うーん。これはに対してtrueを返しますx=iter([1,2])。オブジェクトがジェネレータではなくイテレータであるかどうかを実際にテストしているようです。しかし、おそらく「イテレーター」は、まさに「一般化されたジェネレーター」によってあなたが意味するものです。
Josh O'Brien

3

入力モジュールからイテレーター、より具体的にはジェネレーターを使用できます。

from typing import Generator, Iterator
g = (i for i in range(1_000_000))
print(type(g))
print(isinstance(g, Generator))
print(isinstance(g, Iterator))

結果:

<class 'generator'>
True
True

1
実用的なソリューションの場合は+1。このビーイングが言った、のためのドキュメントtyping.TypeVarクラスが使用阻止するように見えるisinstanceと連携してtyping、モジュールを:「実行時には、isinstance(x, T)発生しますTypeError一般的に、。isinstance()issubclass()タイプで使用すべきではありません。」
Jasha

2
>>> import inspect
>>> 
>>> def foo():
...   yield 'foo'
... 
>>> print inspect.isgeneratorfunction(foo)
True

これは、関数である場合にのみ機能します。「foo」がジェネレーターオブジェクトの場合、「False」を示します。私の質問を参照してください。ジェネレーターオブジェクトをチェックします。
Pushpak Dagade

2

オブジェクトがジェネレーターになるための次のメソッドがあるかどうかを確認できることはわかっていますが、ジェネレーターだけでなく、オブジェクトのタイプを判別できる方法が欲しいです。

これを行わないでください。それは単に非常に、非常に悪い考えです。

代わりに、次のようにします。

try:
    # Attempt to see if you have an iterable object.
    for i in some_thing_which_may_be_a_generator:
        # The real work on `i`
except TypeError:
     # some_thing_which_may_be_a_generator isn't actually a generator
     # do something else

forループの本体にもTypeErrorsがあるというまれなイベントでは、(1)エラーのスコープを制限する関数を定義するか、(2)ネストされたtryブロックを使用するという選択肢があります。

または、(3)TypeError浮かんでいるこれらのすべてを区別するために、このようなもの。

try:
    # Attempt to see if you have an iterable object.
    # In the case of a generator or iterator iter simply 
    # returns the value it was passed.
    iterator = iter(some_thing_which_may_be_a_generator)
except TypeError:
     # some_thing_which_may_be_a_generator isn't actually a generator
     # do something else
else:
    for i in iterator:
         # the real work on `i`

または、(4)アプリケーションの他の部分を修正して、ジェネレータを適切に提供します。これは多くの場合、これよりも簡単です。


1
あなたのソリューションは、forループの本体によってスローされたTypeErrorsをキャッチします。この望ましくない動作を防ぐ編集を提案しました。
砂丘

私が間違っていなければ、これはよりパイソン的なやり方です。
JAB 2011年

ただし、アイテムのリストを反復処理していて、それらの多くがイテレータよりもイテレータでない場合は、確実に時間がかかる可能性がありますか?
Jakob Bowyer

1
@Jakob Bowyer:例外はifステートメントよりも高速です。そして。そのようなマイクロ最適化は時間の無駄です。イテレーターと非イテレーターの混合バッグを生成するアルゴリズムを修正して、イテレーターのみを生成し、この手間をすべて省いてください。
S.Lott

10
これは、反復可能オブジェクトをジェネレータとして誤って想定します。
balki 2013

1

トルネードウェブサーバーなどを使用している場合、サーバーメソッドは実際にはジェネレーターであり、メソッドではないことに気づいたかもしれません。これは、yieldがメソッド内で機能していないため、他のメソッドを呼び出すことが難しくなり、チェーンされたジェネレーターオブジェクトのプールの管理を開始する必要があるためです。チェーンジェネレーターのプールを管理する簡単な方法は、次のようなヘルプ関数を作成することです。

def chainPool(*arg):
    for f in arg:
      if(hasattr(f,"__iter__")):
          for e in f:
             yield e
      else:
         yield f

次のようなチェーンされたジェネレータを書いています

[x for x in chainPool(chainPool(1,2),3,4,chainPool(5,chainPool(6)))]

出力を生成します

[1, 2, 3, 4, 5, 6]

これは、ジェネレータをスレッドの代替または同様のものとして使用する場合におそらく必要なものです。


1

(私はそれが古い投稿であることを知っています。)モジュールをインポートする必要はありません。プログラムの冒頭で比較するオブジェクトを宣言できます:

gentyp= type(1 for i in "")                                                                                          
       ...
type(myobject) == gentyp
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.