如何检查对象是否是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
您要解决什么实际问题?发布更多上下文,可能会有更聪明的方法。为什么需要知道它是否是发电机?
2011年

7
from types import GeneratorType;type(myobject, GeneratorType)将为“发电机”类的对象提供正确的结果。但是正如Daenyth所暗示的那样,这不一定是正确的方法。
JAB

7
如果要检查__next__,则实际上是在接受任何迭代器,而不仅仅是生成器-这很可能是您想要的。

2
通常,由于希望迭代同一集合多次,因此真正了解某个东西是否是生成器的真正目的是能够避免它们。
2013年

2
对于想知道用例的人来说,当您需要了解迭代器是否将被使用时(例如,如果您的函数接受任何迭代器但需要多次迭代,则需要在迭代之前实现它),这可能会很有用
wbadart

Answers:


227

您可以从以下类型使用GeneratorType:

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

5
不幸的是,这不适用于生成器类(例如,映射或过滤器对象)。
里卡多·克鲁兹

也许isinstance(gen, (types.GeneratorType, map, filter))对检测map和诊断也很有用filter。但是,这仍然不包括其他可迭代的对象和迭代器。
jlh

38

你的意思是发电机功能?使用inspect.isgeneratorfunction

编辑:

如果需要生成器对象,可以使用JAB在其注释中指出的inspect.isgenerator


1
生成器函数不是生成器对象;参见@utdemir的答案
Piotr

5
@Piotr:在这种情况下,请使用inspect.isgenerator
JAB

@ JAB,@ Piotr:反映了OP可能意味着的所有可能性,感谢JAB :)
mouad 2011年

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如果您检查例如izipiterable ,它将返回。检查通用生成器的另一种方法是使用此函数:

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

1
嗯 对于,返回true x=iter([1,2])。在我看来,它实际上是在测试对象是否是迭代器,而不是生成器。但是也许“迭代器”正是您所说的“广义生成器”的意思。
乔什·奥布莱恩

3

您可以使用输入模块中的Iterator或更具体地说,使用Generator 。

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 2011年

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。我建议进行修改,以防止这种不良行为。
沙丘

如果我没有记错的话,这是更Python化的方式。
JAB

虽然,如果您要遍历项目列表,并且其中有更多的不是迭代器而不是迭代器,那么这肯定会花费更长的时间吗?
雅各布·鲍耶

1
@Jakob Bowyer:异常比if语句快。和。这种微优化是浪费时间。修复产生迭代器和非迭代器混合包的算法,以仅生成迭代器,从而避免所有麻烦。
S.Lott

10
这会错误地假定任何可迭代的生成器。
巴尔基

1

如果您使用的是Tornado Web服务器或类似服务器,则可能会发现服务器方法实际上是生成器,而不是方法。这使得很难调用其他方法,因为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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.