如何在Python中检查一个对象是否是生成器对象?

220
在Python中,我如何检查一个对象是否是生成器对象?
尝试一下。
>>> type(myobject, generator)

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

我知道我可以检查对象是否具有__next__方法来判断它是否为生成器,但我想找到一种方法来确定任何对象的类型,而不仅仅是生成器。

4
你想解决的实际问题是什么?提供更多背景信息,也许有更明智的方式。为什么你需要知道它是否是一个发电机? - Daenyth
8
from types import GeneratorType;type(myobject, GeneratorType)可以正确地返回类'generator'的对象。但正如Daenyth所暗示的,这并不一定是正确的方法。 - JAB
8
如果你正在检查__next__,那么你实际上是接受任何迭代器,而不仅仅是生成器——这很可能是你想要的。 - user395760
2
哦,稍作更正我的上一条评论:那可能应该是 isinstance(myobject, GeneratorType) - JAB
3
往往,了解某物是否为生成器的真正目的是为了能够避免它们,因为希望多次迭代同一集合。 - Ian
3
对于那些想知道使用情况的人来说,这可能在你需要知道迭代器是否被消耗时很有用(例如,如果你的函数接受任何迭代器但需要迭代多次,则需要在迭代之前将其实例化)。 - wbadart
10个回答

306
你可以使用 `types` 中的 `GeneratorType`
>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True

9
很不幸,这对于生成器类(例如 map 或 filter 对象)无效。 - Ricardo Magalhães Cruz
也许 isinstance(gen, (types.GeneratorType, map, filter)) 可以用来检测 mapfilter。但这仍然不包括其他可迭代对象和迭代器。 - jlh
isinstance({1:2}.values(),types.GeneratorType)==False - CS QGB
@RicardoMagalhãesCruz,这不是“生成器类”。没有所谓的“生成器类”。你说的是迭代器,但它并不是一种类型,它只是意味着你实现了迭代器协议,即定义了__iter____next__ - juanpa.arrivillaga
似乎对异步生成器无效 type: <async_generator object Service.continue at 0x7f1cce523dc0> isinstance(result, types.GeneratorType) -> False. - undefined

49

1
生成器函数不是生成器对象;请参见@utdemir的答案。 - Piotr Findeisen
7
何时使用 inspect.isgenerator 可以使用该函数。 - JAB
@JAB,@Piotr:考虑到问题提出者可能的意思,非常感谢JAB :) - mouad
4
注意:如果您只需要此测试,可以使用@utdemir解决方案,因为inspect.isgenerator仅是isinstance(object, types.GeneratorType)的简写。请注意,翻译尽力保持原意并使其更通俗易懂,但不包括解释或其他额外内容。 - bufh
请参考@RobertLujo的回答,了解生成器对象和生成器函数之间的区别。https://dev59.com/tWw15IYBdhLWcg3wv-ba#32380774 - industryworker3595112
虽然这不是OP的问题(所以没有点赞),但我在搜索“确切”的答案时来到了这里,但这个页面成为了最佳的备选搜索结果。想知道一个给定的函数是否是生成器函数。 - BarryPye

42

我认为区分生成器函数生成器(生成器函数的结果)非常重要:

>>> 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中,在调用函数之前是否有一种方法可以检查该函数是否为“生成器函数”?


17
inspect.isgenerator函数适用于检查纯生成器(即类“generator”的对象)。但是,如果你检查一个zip可迭代对象,它会返回False。检查广义生成器的另一种方法是使用这个函数:
def isgenerator(iterable):
    return hasattr(iterable, '__iter__') and not hasattr(iterable, '__len__')

1
嗯。对于 x=iter([1,2]),它返回 true。在我看来,它实际上是测试一个对象是否为迭代器,而不是生成器。但也许“迭代器”正是你所说的“广义生成器”。 - Josh O'Brien
1
我最喜欢的解决方案是,需要注意的是它不将range视为生成器(从技术上讲是正确的),但对我来说很烦人,因为在py2和py3中range具有不同的类型。 - Orwellophile
dict.values()是一个生成器,但是具有__len__方法。 - CS QGB

9
您可以使用typing模块中的迭代器或更具体地说,生成器。
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

5
对于一个可行的解决方案点个赞。话虽如此,typing.TypeVar类的文档似乎不鼓励在与typing模块一起使用时使用isinstance: "在运行时,isinstance(x, T)将抛出TypeError。通常情况下,不应该使用isinstance()issubclass()来处理类型。" - Jasha
自 Python 3.9 版本起,此文档中的 typing.Generator 已被弃用。 - Gringo Suave

7

(我知道这是一篇旧文章。)无需导入模块,您可以在程序开头声明一个对象以进行比较:

gentyp= type(1 for i in "")                                                                                          
       ...
type(myobject) == gentyp

我喜欢这个,因为一旦解释清楚,它就非常自包含和明显。 - Tom Swirly
同时,它在MicroPython中也可以工作! - Damien

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

1
只有当它是一个函数时才有效。如果'foo'是一个生成器对象,它会显示“False”。请参阅我的问题,我想对生成器对象进行检查。 - Pushpak Dagade

2

如果您正在使用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
我知道我可以检查对象是否具有next方法来确定它是否为生成器,但我希望找到一种方法来确定任何对象的类型,而不仅仅是生成器。
不要这样做。这只是一个非常非常糟糕的想法。
相反,请这样做:
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 循环体中也出现了 TypeError,有几种选择:(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。我提出了一个编辑,可以防止这种不良行为。 - Dunes
如果我没记错的话,这是更符合Python风格的做法。 - JAB
尽管如此,如果您正在迭代一系列项目,并且非迭代器的项目比迭代器的项目更多,则这肯定会花费更长时间? - Jakob Bowyer
1
@Jakob Bowyer:异常比if语句更快。而且,这种微观优化是浪费时间。修复生成混合迭代器和非迭代器的算法,只生成迭代器并节省所有这些痛苦。 - S.Lott
13
这会错误地将任何可迭代对象都视为生成器。 - balki
1
通常不打算在这种代码中进行迭代的原始类型,如字符串和字节,是“可迭代”的逻辑的例外。基本上这是“应该是Pythonic的”...但实际上几乎从来没有起作用。其中最重要的是Python异常通常不够具体。 - Erik Aronesty

0

这是一个有点老的问题,但是我正在寻找类似的解决方案,但是针对异步生成器类,所以您可能会发现这很有用。

根据utdemir的回复:

import types
isinstance(async_generator(), types.AsyncGeneratorType)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接