Consider the following code:
from itertools import chain list(chain(42))
I am passing a non-iterable as an argument to
chain
and little surprisingly, I get exactly this error:TypeError: 'int' object is not iterable
(Passing to
list
is only necessary becausechain
does not evaluate its arguments until the actual iteration.)If I use
chain
correctly, I can unpack the result as function argument:from itertools import chain foo = lambda x: x foo(*chain([42]))
This runs without errors.
Now, consider the combination of the two above cases, i.e., a chain with a non-iterable argument unpacked as function arguments:
from itertools import chain foo = lambda x: x foo(*chain(42))
As expected, this fails. In Python 3 this throws the same error as the first case. However, in Python 2.7.12, the error thrown is:
TypeError: <lambda>() argument after * must be an iterable, not itertools.chain
This does not make any sense to me.
itertools.chain
clearly is an iterable type:isinstance(chain(42),collections.Iterable)
yieldsTrue
. Also, it did not cause any problem in the second example. I would expect a similar error message as in case 2 or Python 3. What is the explanation for this error message?

- 4,234
- 1
- 28
- 54
-
It's also worth noting that `chain(42)` doesn't throw an error in Python2 until you try to iterate over the result. – iafisher Apr 10 '17 at 14:27
-
@iafisher: That’s why I wrapped `list` around it. Also see my edit. – Wrzlprmft Apr 10 '17 at 14:36
-
*`itertools.chain` is an iterable type* <-- only when you've passed it iterables. Garbage in, garbage out. – wim Apr 10 '17 at 14:55
-
1@wim That doesn't explain why Python 2 swallows the underlying exception whereas Python 3 does not. And `itertools.chain(x)` is always iterable, in the sense that it supports Python's iteration protocol. It's just that following that protocol fails if the original argument is not iterable itself. – Florian Brucker Apr 10 '17 at 15:01
-
@wim: `chain` is also an iterable with garbage arguments, otherwise the following would fail earlier than it does: `for x in chain(range(10),11): print(x)`. — *it's not obvious what exactly the problem is or what part needs any further explanation.* – The error message does not make any sense to me. If it makes sense to you, enlighten me with an answer (also see my edit). – Wrzlprmft Apr 11 '17 at 07:47
-
OK, so you just want to know why the error message is different? This is arguably a "RTFS" question, but I've posted an answer for you.. – wim Apr 11 '17 at 16:42
1 Answers
The behaviour you are seeing is an attempt to give a clearer error message about what went wrong with the function call.
Python 2.7's way of determining if an object is iterable is just attempting to iterate it, and then catch the TypeError
exception if necessary. It's not actually implemented in Python code, but that's still what happens in handling the function call syntax. Note: this has nothing to do with lambda
, and a plain old def
would have illustrated the example as well.
The function call is handled in CPython 2.7 by this C code:
static PyObject *
ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk)
{
... snip ...
t = PySequence_Tuple(stararg);
if (t == NULL) {
if (PyErr_ExceptionMatches(PyExc_TypeError) &&
/* Don't mask TypeError raised from a generator */
!PyGen_Check(stararg)) {
PyErr_Format(PyExc_TypeError,
"%.200s%.200s argument after * "
"must be an iterable, not %200s",
PyEval_GetFuncName(func),
PyEval_GetFuncDesc(func),
stararg->ob_type->tp_name);
}
goto ext_call_fail;
... snip ...
}
I've truncated the code for brevity to show the relevant block: the starargs are iterated into a tuple, and if that fails with PyExc_TypeError
then a new error is raised with the type and message matching what you've seen.
In Python 3, the function call C code was cleaned up and simplified significantly. Actually the ext_do_call
function doesn't even exist any more, it was likely removed during implementation of PEP 3113. Now the exception from iterating a broken chain bubbles up unhandled. If you want to poke around in the current call code, you may start digging in Python/ceval.c::do_call_core
.

- 338,267
- 99
- 616
- 750