Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

isinstance(foo, types.GeneratorType) or inspect.isgenerator(foo)?

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

like image 959
HorseloverFat Avatar asked Oct 21 '13 16:10

HorseloverFat


3 Answers

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.

like image 183
Bakuriu Avatar answered Nov 17 '22 00:11

Bakuriu


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.

like image 27
SingleNegationElimination Avatar answered Nov 16 '22 23:11

SingleNegationElimination


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.

like image 1
Corley Brigman Avatar answered Nov 17 '22 00:11

Corley Brigman