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

2024-05-26 09:19:11 发布

您现在位置:Python中文网/ 问答频道 /正文

很常见的模式是遍历列表的一部分并搜索/选择/处理项目:

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

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

使用itertools.islice可以解决这个问题。你知道吗

问:为什么这不是经常使用和教导像是教导危险的可变默认函数参数等。?你知道吗

更新:

正如在评论中指出的,“itertools.islice代表iterables,而不是list。在到达第n个元素之前,它必须迭代很多次”。你知道吗

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

你可以做:

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

有没有一种pythonic方法可以迭代序列的一部分而不创建它的副本?。因为本例中不支持负索引,而且它不像for item in sequence那么好。你知道吗


Tags: 项目in列表for模式itemarraydo
1条回答
网友
1楼 · 发布于 2024-05-26 09:19:11

切片之所以是Pythonic,即使它创建了一个副本,是因为切片的成本通常是微不足道的宏伟计划的事情。如果从list中切片10000个项目,只需迭代并丢弃它们,其成本是:

  1. 为10000个指针分配空间(40K-80K字节)
  2. 通过指针从源复制到新空间
  3. 递增每个指针的引用计数
  4. 在此处使用内容
  5. 递减每个指针的引用计数
  6. 释放空间

在C级,相对于解释器本身的开销而言,分配几KB的RAM和递增/递减几千个整数是相当微不足道的(请记住,无论您做什么,当您循环并将每个元素存储在一个命名变量中时,引用计数都会更新);几乎任何你用结果list做的事情都会比创建和销毁它的成本更高,特别是在切片很小的情况下(用任何替换它的解释器开销可能会超过切片的成本)。你知道吗

如果拷贝确实是个问题,而且start索引非常重要(因此islice无法直接跳到start点使得它不可行,并且片足够大,您可以合理地担心内存),那么我所知的最Pythonic的方法基本上就是您提供的示例循环:

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

如果速度太慢(重复索引会在CPython引用解释器中产生惊人的解释器开销),那么最适合python的方法就是将索引推到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__这样的特殊方法是非常难看的)。你知道吗

相关问题 更多 >