7

我正在努力理解下面的代码是如何工作的。它来自http://docs.python.org/library/itertools.html#itertools.izip_longest,是 izip_longest 迭代器的纯 python 等价物。我对哨兵功能特别困惑,它是如何工作的?

def izip_longest(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
        yield counter()         # yields the fillvalue, or raises IndexError
    fillers = repeat(fillvalue)
    iters = [chain(it, sentinel(), fillers) for it in args]
    try:
        for tup in izip(*iters):
            yield tup
    except IndexError:
        pass
4

3 回答 3

6

好的,我们可以做到这一点。关于哨兵。该表达式([fillvalue]*(len(args)-1))创建一个列表,其中包含每个可迭代 in 的一个填充值args,减一。所以,对于上面的例子['-']counter然后分配该pop列表的 -function。sentinel它本身是一个生成器,它在每次迭代时从该列表中弹出一个项目。您可以对每个返回的迭代器进行sentinel一次迭代,并且它将始终 yield fillvalue。由返回的所有迭代器产生的项目总数sentinellen(args) - 1(感谢 Sven Marnach 澄清这一点,我误解了它)。

现在看看这个:

iters = [chain(it, sentinel(), fillers) for it in args]

这就是诀窍。iters是一个列表,其中包含 中每个可迭代的迭代器args。这些迭代器中的每一个都执行以下操作:

  1. 遍历对应的可迭代 from 中的所有项目args
  2. 遍历 sentinel 一次,产生fillvalue.
  3. 永远重复fillvalue

现在,如前所述,我们只能len(args)-1在它抛出一个IndexError. 这很好,因为其中一个可迭代对象是最长的。所以,当我们提到IndexError被提升的时候,这意味着我们已经完成了对最长可迭代 in 的迭代args

不客气。

PS:我希望这是可以理解的。

于 2011-03-14T12:55:07.537 回答
5

该函数返回只产生一次的sentinel()迭代器。由返回的所有迭代器产生fillvalue的 s 的总数限制为,其中是传递给 的迭代器的数量。在这个数量的s 用完后,对返回的迭代器的进一步迭代将引发.fillvaluesentinel()n-1nizip_longest()fillvaluesentinel()IndexError

此函数用于检测是否所有迭代器都已用尽:每个迭代器都chain()带有一个由 . 返回的迭代器sentinel()。如果所有迭代器都用完,则返回的迭代器sentinel()将被迭代n第 th 次,从而导致, 依次IndexError触发结束。izip_longest()

到目前为止,我解释了它是做什么sentinel()的,而不是它是如何工作的。当izip_longest()被调用时,sentinel()会评估 的定义。在评估定义时,也会评估默认参数sentinel(),每次调用一次izip_longest()。代码相当于

fillvalue_list = [fillvalue] * (len(args)-1)
def sentinel():
    yield fillvalue_list.pop()

将它存储在默认参数中而不是封闭范围内的变量中只是一种优化,就像.pop在默认参数中包含一样,因为它每次迭代返回的迭代器时都无需查找它sentinel()

于 2011-03-14T12:58:14.347 回答
2

的定义sentinel几乎等同于

def sentinel():
    yield ([fillvalue] * (len(args) - 1)).pop()

除了它获取pop绑定的方法(一个函数对象)作为默认参数。默认参数在函数定义时进行评估,因此每次调用izip_longest一次而不是每次调用一次sentinel。因此,函数对象“记住”列表[fillvalue] * (len(args) - 1),而不是在每次调用中重新构造它。

于 2011-03-14T12:55:31.533 回答