列表的这种“贪婪”+= 行为是否得到保证?

Kel*_*ndy 15 python list language-lawyer

我偶尔会使用“技巧”来通过自身的映射版本来扩展列表,例如有效地计算 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]
Run Code Online (Sandbox Code Playgroud)

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

powers = [1]
for value in map(mul, [2] * 10, powers):
    powers.append(value)
Run Code Online (Sandbox Code Playgroud)

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

powers = [1]
powers += list(map(mul, [2] * 10, powers))
Run Code Online (Sandbox Code Playgroud)

是否在某个地方保证它能像以前那样工作?我检查了可变序列类型s += t文档,但除了暗示和的等价性之外,它没有说太多s.extend(t)。它确实引用了MutableSequence,其源代码包括:

    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)
Run Code Online (Sandbox Code Playgroud)
    def __iadd__(self, values):
        self.extend(values)
        return self
Run Code Online (Sandbox Code Playgroud)

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

Ray*_*ger 7

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

\n

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

\n
>>> powers = [1]\n>>> powers.extend(2*x for x in islice(powers, 10))\n>>> powers\n[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]\n
Run Code Online (Sandbox Code Playgroud)\n

虽然我还没有找到+=or的保证extend,但我们确实保证列表迭代器允许在迭代时发生突变。\xc2\xb9 因此,这段代码是有坚实基础的:

\n
>>> powers = [1]\n>>> for x in powers:\n        if len(powers) == 10:\n            break\n        powers.append(2 * x)\n\n>>> powers\n[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]\n
Run Code Online (Sandbox Code Playgroud)\n

\xc2\xb9 请参阅表后面的第二段:\n https://docs.python.org/3/library/stdtypes.html#common-sequence-operations

\n
\n

可变序列上的正向和反向迭代器使用索引\n访问值。即使基础序列发生变异,该索引也将继续向前(或\n向后)前进。仅当遇到 IndexError 或 StopIteration(或当索引降至零以下时),迭代器才会终止。

\n
\n

  • 少数几个“我不知道有什么保证”* 是有用的声明的人之一:-)。我在哪里可以找到您提到的其他保证?您怎么看:考虑到这是预期的行为,并且野外代码可能依赖于它,可以安全地假设它可以依赖吗?你觉得卡尔的[“不应该”](/sf/ask/5050883351/#comment127489363_72155476)怎么样?当我使用它时,它是为了短/快速代码,我确实喜欢使用它并且不同意我不应该使用它。 (2认同)
  • 迭代时改变列表是已经完成的事情。应该或不应该是个人决定。通常的规则是坚持使用简单而明显的代码,除非有一些实际原因需要不那么传统或更棘手的代码。如果需要进行黑客攻击,至少需要发表评论来解释它的作用以及为什么首选它。 (2认同)