在遍历切片时避免创建新列表的 Pythonic 方法?

Pythonic way to avoid creation of a new list when iterating over a slice?

很常见的模式是遍历列表的一部分和search/select/process项:

for item in array[start:end]:
   # do something

据我所知,这会从 array[start:end] 复制项目创建一个新列表,遍历它,然后临时列表被垃圾收集。这看起来像是在浪费 CPU 用法。

使用 itertools.islice 解决这个问题。

问题:为什么这没有被广泛使用,并且像被教导可变默认函数参数的危险等那样被教导?

更新:

正如评论中指出的那样,“itertools.islice 适用于可迭代对象,而不是列表。在到达第 n 个元素之前,它必须迭代很多次”。

我想 islice 不是一个很好的例子,但是 创建一个临时列表只是为了遍历它是一个存在的问题。不是吗?

你可以这样做:

for i in xrange(start, end):
    item = array[i]
    # do something with the item

是否有 pythonic 方法来迭代序列的一部分而不创建它的副本?。因为这个例子中的负索引是不支持的,而且不如for item in sequence.

切片是 Pythonic 的原因,即使它创建了一个副本,是因为切片的成本在事物的宏伟计划中通常是微不足道的。如果您从 list 中切出 10,000 个项目,只是为了迭代它们并丢弃它们,其成本是:

  1. 为 10,000 个指针(40K - 80K 字节)分配 space
  2. 正在将指针从源复制到新的 space
  3. 增加每个指针的引用计数
  4. 使用这里的内容
  5. 递减每个指针的引用计数
  6. 释放 space

在 C 级别,分配几 KB 的 RAM 和 incrementing/decrementing 几千个整数相对于解释器本身的开销来说是微不足道的(请记住,无论您做什么,参考当您循环并将每个元素存储在命名变量中时,将发生计数更新);几乎任何你的结果list都会比创建和销毁它的成本更高,特别是如果切片很小(解释器开销任何你可以用超过切片成本的东西来替换它。

如果复制确实有问题,并且start索引是非平凡的(所以islice不能直接跳到start点,所以它不是-可行,并且切片足够大,您有理由担心内存),我所知道的最 Pythonic 方法基本上就是您提供的示例循环:

for i in xrange(start, end):
    item = array[i]
    # do something with the item

如果那太慢了(重复索引在 CPython 参考解释器中涉及数量惊人的解释器开销),最 Pythonic 的替代方法是将索引推送到 C 层:

from future_builtins import map  # Only needed on Python 2 to get Py3 generator based map

for item in map(array.__getitem__, xrange(start, end)):  # range on Python 3
    # do something with the item

这避免了临时的list,换取更高的每个元素成本(因为索引无法完全避免,只是隐藏在C层,不涉及字节码执行)。虽然绝对不那么 Pythonic(map 通常不被接受,并且显式引用像 __getitem__ 这样的特殊方法是非常丑陋的)。