[英]Why does unpacking a chain with a non-iterable argument raise this error?
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: 我将一个不可迭代的参数传递给
chain
并且毫不奇怪,我得到了这个错误:
TypeError: 'int' object is not iterable
(Passing to list
is only necessary because chain
does not evaluate its arguments until the actual iteration.) (仅因为
chain
在实际迭代之前才评估其参数,所以传递到list
是唯一必要的。)
If I use chain
correctly, I can unpack the result as function argument: 如果正确使用
chain
,则可以将结果作为函数参数解压缩:
from itertools import chain foo = lambda x: x foo(*chain([42]))
This runs without errors. 这没有错误运行。
Now, consider the combination of the two above cases, ie, 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.
在Python 3中,这将引发与第一种情况相同的错误。 However, in Python 2.7.12, the error thrown is:
但是,在Python 2.7.12中,抛出的错误是:
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)
yields True
. itertools.chain
显然是一个可迭代的类型: isinstance(chain(42),collections.Iterable)
产生True
。 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?
我期望与情况2或Python 3类似的错误消息。此错误消息的解释是什么?
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. Python 2.7确定对象是否可迭代的方法只是尝试对其进行迭代,然后在必要时捕获
TypeError
异常。 It's not actually implemented in Python code, but that's still what happens in handling the function call syntax. 它实际上并没有在Python代码中实现,但这仍然是处理函数调用语法时发生的事情。 Note: this has nothing to do with
lambda
, and a plain old def
would have illustrated the example as well. 注意:这与
lambda
无关,一个普通的def
也将说明该示例。
The function call is handled in CPython 2.7 by this C code : 这个C代码在CPython 2.7中处理了函数调用:
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. 为了简洁起见,我已将代码截断以显示相关的块:将starargs迭代为一个元组,如果
PyExc_TypeError
失败,则类型和消息与您所看到的匹配的消息将引发新的错误。
In Python 3, the function call C code was cleaned up and simplified significantly. 在Python 3中,函数调用C代码已被清理和简化。 Actually the
ext_do_call
function doesn't even exist any more, it was likely removed during implementation of PEP 3113 . 实际上,
ext_do_call
函数甚至不再存在,它可能在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
. 如果您想在当前的调用代码中四处浏览,则可以开始在
Python/ceval.c::do_call_core
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.