"greedy" += 列表的行为是否得到保证?

Is this "greedy" += behavior of lists guaranteed?

我偶尔会使用“技巧”通过自身的映射版本扩展列表,例如高效计算 2 的幂:

from operator import mul

powers = [1]
powers += map(mul, [2] * 10, powers)

print(powers)   # prints [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

这依赖于 += 立即将 map 中的每个值附加到列表中,以便 map 找到它并继续该过程。换句话说,它需要像这样工作:

powers = [1]
for value in map(mul, [2] * 10, powers):
    powers.append(value)

而不是像这样首先计算和存储整个右侧,其中 powers 最终是 [1, 2]:

powers = [1]
powers += list(map(mul, [2] * 10, powers))

是否在某处保证它像它一样工作?我检查了 Mutable Sequence Types documentation but it doesn't say much about it other than implying equivalence of s += t and s.extend(t). It does refer to MutableSequence, whose source code 包括这个:

    def extend(self, values):
        'S.extend(iterable) -- extend sequence by appending elements from the iterable'
        if values is self:
            values = list(values)
        for v in values:
            self.append(v)
    def __iadd__(self, values):
        self.extend(values)
        return self

这确实表明它确实 应该 可以像我想要的那样工作,但是一些源代码并不像保证那样安全在文档中。

我没有看到任何保证贪婪行为的测试或文档;但是,我确实认为这是预期的行为,并且野外的代码依赖于它。

FWIW,+= 与列表等同于 list.extend(),所以你的“技巧”归结为:

>>> powers = [1]
>>> powers.extend(2*x for x in islice(powers, 10))
>>> powers
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

虽然我还没有找到 +=extend 的保证,但我们确实可以保证列表迭代器在迭代时允许突变。¹因此,这段代码是有根据的:

>>> powers = [1]
>>> for x in powers:
        if len(powers) == 10:
            break
        powers.append(2 * x)

>>> powers
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

¹ 请参阅 table 之后的第二段: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations:

Forward and reversed iterators over mutable sequences access values using an index. That index will continue to march forward (or backward) even if the underlying sequence is mutated. The iterator terminates only when an IndexError or a StopIteration is encountered (or when the index drops below zero).