2017-04-10 49 views
0
  1. 考虑下面的代码:为什么用一个不可迭代的参数解链一个链会引发这个错误?

    from itertools import chain 
    list(chain(42)) 
    

    我传递一个非迭代器作为参数传递给chain和小奇怪的是,我得到的正是这种错误:

    TypeError: 'int' object is not iterable 
    

    (传递到list仅仅是必要的,因为chain在实际迭代之前不会评估它的参数。)

  2. 如果我正确地使用chain,我可以解压结果作为函数的参数:

    from itertools import chain 
    foo = lambda x: x 
    foo(*chain([42])) 
    

    这将运行没有错误。

  3. 现在,考虑的上述两种情况的组合,即,与非可迭代参数链解压作为函数参数:

    from itertools import chain 
    foo = lambda x: x 
    foo(*chain(42)) 
    

    正如预期的那样,这将失败。在Python 3中,这会引发与第一种情况相同的错误。然而,在Python 2.7.12中,抛出的错误是:

    TypeError: <lambda>() argument after * must be an iterable, not itertools.chain 
    

    这对我没有任何意义。明显是可迭代类型:isinstance(chain(42),collections.Iterable)产量为True。另外,它在第二个例子中没有引起任何问题。我期望类似于错误消息2或Python 3中的错误消息。此错误消息的解释是什么?

+0

另外值得注意的是,在您尝试迭代结果之前,'chain(42)'不会在Python2中引发错误。 – iafisher

+0

@iafisher:这就是为什么我在它周围包裹了'list'的原因。另请参阅我的编辑。 – Wrzlprmft

+0

*'itertools.chain'是一个可迭代的类型* < - 只有当您通过迭代才能通过。垃圾进垃圾出。 – wim

回答

0

您所看到的行为是企图给什么地方发生了错误的函数调用更清晰的错误消息。

Python 2.7确定对象是否可迭代的方法是试图迭代它,然后在必要时捕获TypeError异常。它实际上并未在Python代码中实现,但这仍然是处理函数调用语法时发生的事情。 注意:这与lambda没有任何关系,普通的旧的def也可以说明这个例子。

函数调用在CPython的2.7 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 ... 
} 

我已经被截断的代码简洁,以显示相关的块:在starargs进行迭代成一个元组,如果失败与PyExc_TypeError那么随着类型和消息匹配你所看到的,会引发一个新的错误。

在Python 3中,函数调用C代码被清理并显着简化。其实ext_do_call函数甚至不存在,它可能在执行PEP 3113时被删除。现在来自迭代断链的异常冒泡未处理。如果您想在当前的通话代码中进行搜索,您可以开始使用Python/ceval.c::do_call_core进行挖掘。

相关问题