225

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

Trying

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

6
  • 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? Commented Jun 20, 2011 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. Commented Jun 20, 2011 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. Commented Jun 20, 2011 at 19:46
  • 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. Commented Mar 25, 2016 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) Commented Jun 26, 2017 at 18:22

10 Answers 10

315

You can use GeneratorType from types:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True
Sign up to request clarification or add additional context in comments.

5 Comments

This unfortunately doesn't work for generator classes (for example, map or filter objects).
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.
isinstance({1:2}.values(),types.GeneratorType)==False
@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__
Doesn't seem to work for async generators type: <async_generator object Service.continue at 0x7f1cce523dc0> isinstance(result, types.GeneratorType) -> False.
52

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.

6 Comments

generator function is not generator object; see @utdemir's answer
@Piotr: In which case you use inspect.isgenerator.
@JAB, @Piotr: Reflected to address all the possibilities of what the OP can mean , thanks JAB :)
Note: if you only need this test, you can avoid a small overhead by using @utdemir solution because inspect.isgenerator is only a shorthand to: isinstance(object, types.GeneratorType).
See @RobertLujo answer for distinction between generator object and generator function. stackoverflow.com/a/32380774/3595112
|
45

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?

Comments

18

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 zip 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__')

3 Comments

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".
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
dict.values() is generator ,but has` __len__`
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

2 Comments

+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."
docs.python.org/3/library/… Deprecated since version 3.9
7

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

2 Comments

I like this one because it's so self-contained and obvious once it is explained.
Also, it works in micropython !
4
>>> import inspect
>>> 
>>> def foo():
...   yield 'foo'
... 
>>> print inspect.isgeneratorfunction(foo)
True

1 Comment

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

Comments

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.

6 Comments

Your solution will catch TypeErrors thrown by the body of the for loop. I've proposed an edit that would prevent this undesirable behaviour.
This is the more Pythonic way of doing it, if I'm not mistaken.
@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.
This would mistakenly assume any iterable as a generator.
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.
|
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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.