It seems that there are two ways in Python to test whether an object is a generator:
import types
isinstance(foo, types.GeneratorType)
or:
import inspect
inspect.isgenerator(foo)
In the spirit of "There should be one-- and preferably only one --obvious way to do it.", is one of these ways recommended over the other (presumably they do the same thing...if not, please enlighten me!)?
They are 100% equivalent:
>>> print(inspect.getsource(inspect.isgenerator))
def isgenerator(object):
"""Return true if the object is a generator.
Generator objects provide these attributes:
__iter__ defined to support interation over container
close raises a new GeneratorExit exception inside the
generator to terminate the iteration
gi_code code object
gi_frame frame object or possibly None once the generator has
been exhausted
gi_running set to 1 when generator is executing, 0 otherwise
next return the next item from the container
send resumes the generator and "sends" a value that becomes
the result of the current yield-expression
throw used to raise an exception inside the generator"""
return isinstance(object, types.GeneratorType)
I'd say that using isinstance(object, types.GeneratorType)
should be the preferred way since it's clearer and simpler.
Also inspect.isgenerator
was only added in python2.6, which means that using isinstance
is more backward compatible.
They probably added the isgenerator
function for symmetry isgeneratorfunction
which does something different.
You can do type checking, but you probably don't want to check for just generators. what you really want is to check for 'iterators.', or rather, you want two iterators.
import collections, itertools
def cheap_tee(foo):
if isinstance(foo, collections.Iterator):
# this is already an iterator, we need to 'tee' it
return itertools.tee(foo)
elif isinstance(foo, collections.Iterable):
# this is already an iterable, get two iterators from it:
return iter(foo), iter(foo)
raise TypeError("Don't know how to cheaply copy these", foo)
This will then work on anything that is remotely iterable, not just generator expressions. Some types will provide custom iterators that work on data structures that are not easily expressed in terms of generator expressions or generators, or are implemented as iterators in C. Either may also provide a __copy__
mechanism that itertools.tee
can actually use and won't bother duplicating work, either. Only if its' really already an iterator that tee
can't copy for you will it use space, doing all of the crystalizing for you.
you should be able to do:
try:
x = possible_generator.next()
mylist = [x] + list(possible_generator)
except:
pass
This will differentiate between generators and built-in iterables; however, if you have a custom class that is list-like but also implements next, then it would fail.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With