218

In python, how do I check if an object is a generator object?

Trying this -

>>> type(myobject, generator)

gives the error -

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

(I know I can check if the object has a next method for it to be a generator, but I want some way using which I can determine the type of any object, not just generators.)

Pushpak Dagade
  • 6,280
  • 7
  • 28
  • 41
  • 4
    What actual problem are you trying to solve? Post more context, there may be a smarter way. Why do you need to know if it's a generator? – Daenyth Jun 20 '11 at 19:43
  • 8
    `from types import GeneratorType;type(myobject, GeneratorType)` will give you the proper result for objects of class 'generator'. But as Daenyth implies, that isn't necessarily the right way to go. – JAB Jun 20 '11 at 19:45
  • 8
    If you're checking for `__next__`, you're actually accepting any iterator, not just generators - which is very likely what you want. –  Jun 20 '11 at 19:46
  • 2
    Oh, slight correction to my previous comment: that should probably be `isinstance(myobject, GeneratorType)`. – JAB Jun 20 '11 at 19:52
  • 3
    As often as not, the real point of knowing whether something is a generator is to be able to avoid them, on account of desiring to iterate over the same collection multiple times. – Ian Mar 25 '16 at 21:26
  • 3
    For people wondering about the use case, this could be useful when you need to know if the iterator will be consumed (eg if your function accepts any iterator but needs to iterate more than once, you'll want to materialize it before iterating) – wbadart Jun 26 '17 at 18:22

10 Answers10

305

You can use GeneratorType from types:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True
utdemir
  • 26,532
  • 10
  • 62
  • 81
  • 8
    This unfortunately doesn't work for generator classes (for example, map or filter objects). – Ricardo Magalhães Cruz Dec 15 '18 at 18:41
  • Perhaps `isinstance(gen, (types.GeneratorType, map, filter))` is useful to also detect `map` and `filter`. This will still not include other iterables and iterators though. – jlh May 02 '20 at 14:32
  • isinstance({1:2}.values(),types.GeneratorType)==False – CS QGB Apr 28 '21 at 05:34
  • @RicardoMagalhãesCruz that is not a "generator class". There is no such thing as a "generator class". You are talking about *iterators*, but being an iterator is **not a type**, it simply means you implement the iterator protocol, i.e. define `__iter__` and `__next__` – juanpa.arrivillaga Nov 21 '21 at 19:54
49

You mean generator functions ? use inspect.isgeneratorfunction.

EDIT :

if you want a generator object you can use inspect.isgenerator as pointed out by JAB in his comment.

mouad
  • 67,571
  • 18
  • 114
  • 106
  • 1
    generator function is not generator object; see @utdemir's answer – Piotr Findeisen Jun 20 '11 at 19:50
  • 7
    @Piotr: In which case you use [`inspect.isgenerator`](http://docs.python.org/library/inspect.html#inspect.isgenerator). – JAB Jun 20 '11 at 19:52
  • @JAB, @Piotr: Reflected to address all the possibilities of what the OP can mean , thanks JAB :) – mouad Jun 20 '11 at 19:57
  • 3
    Note: if you only need this test, you can avoid a small overhead by using [**@utdemir** solution](http://stackoverflow.com/a/6416585/248390) because `inspect.isgenerator` is only a shorthand to: [`isinstance(object, types.GeneratorType)`](https://hg.python.org/cpython/file/44590c1b264e/Lib/inspect.py#l164). – bufh Apr 05 '16 at 07:21
  • See @RobertLujo answer for distinction between generator object and generator function. http://stackoverflow.com/a/32380774/3595112 – industryworker3595112 Dec 01 '16 at 10:27
  • While not OP's question (so no upvote), I came here while searching for _exactly_ this answer but this page came up as the best alternative search result. Wanted to know if a given function is a generator function. – BarryPye Sep 22 '21 at 16:29
42

I think it is important to make distinction between generator functions and generators (generator function's result):

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

calling generator_function won't yield normal result, it even won't execute any code in the function itself, the result will be special object called generator:

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

so it is not generator function, but generator:

>>> inspect.isgeneratorfunction(generator)
False

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

and generator function is not generator:

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

just for a reference, actual call of function body will happen by consuming generator, e.g.:

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

See also In python is there a way to check if a function is a "generator function" before calling it?

Community
  • 1
  • 1
Robert Lujo
  • 15,383
  • 5
  • 56
  • 73
17

The inspect.isgenerator function is fine if you want to check for pure generators (i.e. objects of class "generator"). However it will return False if you check, for example, a izip iterable. An alternative way for checking for a generalised generator is to use this function:

def isgenerator(iterable):
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
Luca Sbardella
  • 1,114
  • 9
  • 13
  • 1
    Hmm. This returns true for `x=iter([1,2])`. Seems to me it's really testing whether or not an object is an **iterator**, not a generator. But maybe "iterator" is exactly what you mean by "generalised generator". – Josh O'Brien May 16 '14 at 01:36
  • 1
    My favorite solution, though it should be noted that it doesn't count `range` as a generator (technically correct), but annoying for me as `range` has a different type py2 vs 3 – Orwellophile Apr 03 '21 at 12:41
  • dict.values() is generator ,but has` __len__` – CS QGB Apr 28 '21 at 05:37
9

You could use the Iterator or more specifically, the Generator from the typing module.

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))

result:

<class 'generator'>
True
True
user9074332
  • 2,336
  • 2
  • 23
  • 39
  • 5
    +1 for a working solution. This being said, the docs for the `typing.TypeVar` class seem to discourage the use of `isinstance` in conjunction with the `typing` module: "At runtime, `isinstance(x, T)` will raise `TypeError`. In general, `isinstance()` and `issubclass()` should not be used with types." – Jasha Jun 17 '20 at 23:06
  • https://docs.python.org/3/library/typing.html?highlight=typing#typing.Generator Deprecated since version 3.9 – Gringo Suave Jul 21 '22 at 04:09
7

(I know it's an old post.) There is no need to import a module, you can declare an object for comparison at the beginning of the program:

gentyp= type(1 for i in "")                                                                                          
       ...
type(myobject) == gentyp
kantal
  • 2,331
  • 2
  • 8
  • 15
4
>>> import inspect
>>> 
>>> def foo():
...   yield 'foo'
... 
>>> print inspect.isgeneratorfunction(foo)
True
Corey Goldberg
  • 59,062
  • 28
  • 129
  • 143
  • 1
    This works only if it is a function. If 'foo' is a generator object, it shows 'False'. See my question, I want to make checks for generator objects. – Pushpak Dagade Jun 21 '11 at 04:42
2

If you are using tornado webserver or similar you might have found that server methods are actually generators and not methods. This makes it difficult to call other methods because yield is not working inside the method and therefore you need to start managing pools of chained generator objects. A simple method to manage pools of chained generators is to create a help function such as

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

Now writing chained generators such as

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

Produces output

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

Which is probably what you want if your looking to use generators as a thread alternative or similar.

user6830669
  • 161
  • 4
1

I know I can check if the object has a next method for it to be a generator, but I want some way using which I can determine the type of any object, not just generators.

Don't do this. It's simply a very, very bad idea.

Instead, do this:

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

In the unlikely event that the body of the for loop also has TypeErrors, there are several choices: (1) define a function to limit the scope of the errors, or (2) use a nested try block.

Or (3) something like this to distinguish all of these TypeErrors which are floating around.

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`

Or (4) fix the other parts of your application to provide generators appropriately. That's often simpler than all of this.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 1
    Your solution will catch TypeErrors thrown by the body of the for loop. I've proposed an edit that would prevent this undesirable behaviour. – Dunes Jun 20 '11 at 20:21
  • This is the more Pythonic way of doing it, if I'm not mistaken. – JAB Jun 20 '11 at 20:21
  • Although, if you are iterating over a list of items and more of them are not iterators than are iterators then this could take longer surely? – Jakob Bowyer Jun 20 '11 at 21:36
  • 1
    @Jakob Bowyer: Exceptions are faster than `if` statements. And. That kind of micro-optimization is a waste of time. Fix the algorithm that produces a mixed bag of iterators and non-iterators to produce only iterators and save yourself all of this pain. – S.Lott Jun 20 '11 at 22:09
  • 13
    This would mistakenly assume any iterable as a generator. – balki Jul 17 '13 at 17:59
  • 1
    primitives which are typically not intended to be iterated in this sort of code, like strings, and bytes, are an exception to the "this is iterable" logic. basically this is "supposed to be pythonic"... but almost never works in practice. not the least of which is that python exceptions are often not specific enough. – Erik Aronesty Mar 30 '21 at 14:08
0

It's a little old question, however I was looking for similar solution for myself, but for async generator class, so you may find this helpful.

Based on utdemir reply:

import types
isinstance(async_generator(), types.AsyncGeneratorType)
Tatarinho
  • 754
  • 2
  • 11
  • 31