TypeError: type object argument after * must be a sequence, not generator

JulienD picture JulienD · Mar 22, 2013 · Viewed 12.1k times · Source

Why does the following Python code raise an error
TypeError: type object argument after * must be a sequence, not generator
while if I comment the first (useless) line in generator f, everything works fine?

from itertools import izip

def z():
    for _ in range(10): 
        yield _

def f(z):
    for _ in z: pass    # if I comment this line it works! (??)
    for x in range(10):
        yield (x,10*x,100*x,1000*x)

iterators =  izip(*f(z))
for it in iterators:
    print list(it)

N.B. What I am actually trying to do is, with a single generator, return multiple iterators (as many as I will pass to the generator as arguments). The only way I found to do that is to yield tuples and use izip() on them - black magic to me.

Answer

Pavel Anossov picture Pavel Anossov · Mar 22, 2013

This is amusing: you forgot to call z when you passed it to f:

iterators =  izip(*f(z()))

So f tried to iterate over a function object:

for _ in z: pass  # z is a function

This raised a TypeError:

TypeError: 'function' object is not iterable

Python innards caught it and reraised with a confusing error message.

# ceval.c

static PyObject *
ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk)
{
 ...

            t = PySequence_Tuple(stararg);
            if (t == NULL) {
                if (PyErr_ExceptionMatches(PyExc_TypeError)) {
                    PyErr_Format(PyExc_TypeError,
                                 "%.200s%.200s argument after * "
                                 "must be a sequence, not %200s",
                                 PyEval_GetFuncName(func),
                                 PyEval_GetFuncDesc(func),
                                 stararg->ob_type->tp_name);
 ...