【问题标题】:Does it pay off to use a generator as input to sorted() instead of a list-comprehension [duplicate]使用生成器作为 sorted() 的输入而不是列表理解是否值得?
【发布时间】:2012-08-01 10:09:11
【问题描述】:

可能重复:
sorted() using Generator Expressions Rather Than Lists

我们都知道一直使用生成器而不是实例化列表可以节省时间和内存,尤其是在我们大量使用推导式的情况下。

这里有一个问题,请考虑以下代码:

output = SomeExpensiveCallEgDatabase()
results = [result[0] for result in output]
return sorted(results)

对 sorted 的调用将返回结果的排序列表。如下声明结果然后调用 sorted 是更好还是更糟?

results = (result[0] for result in output)

我的猜测是对 sorted() 的调用将遍历生成器并实例化一个列表本身,以便对其运行快速排序或合并排序。所以在这里使用生成器没有任何优势。这个假设正确吗?

【问题讨论】:

  • 我认为没有区别。

标签: python optimization generator list-comprehension sorted


【解决方案1】:

我相信你的假设是正确的,因为没有先将整个列表放在内存中就没有简单的方法来排序集合(至少肯定不会使用默认排序算法,如果我没记错的话,TimSort)。

检查一下: sorted() using Generator Expressions Rather Than Lists

为了创建新的 List,内置的 sorted 方法使用PySequence_List

PyObject* PySequence_List(PyObject *o) 返回值:新引用。 返回与任意序列具有相同内容的列表对象 ○。返回的列表保证是新的。

两种方法的优缺点:

内存方面:

返回的列表是用于排序版本的列表,因此这意味着在这种情况下,在任何给定时间,只有一个列表完全存储在内存中,使用生成器版本。

这使得生成器版本的内存效率更高。

速度:

这里有完整列表的版本获胜。

要基于生成器创建新列表,必须创建一个空列表(或最多使用第一个元素),并将每个后续元素附加到列表中,并可能会引发重新调整维度的步骤。

要基于前一个列表创建一个新列表,列表的大小是事先知道的,因此可以一次分配并分配每个条目(可能这里还有其他优化工作,但我可以不要备份)。

所以关于速度,列表胜出。

“什么是最好的”的答案归结为任何工程领域中最常见的答案...这取决于...

【讨论】:

  • 该链接表明生成器表达式优越,因为内存中仅存在sorted 正在操作的列表的副本。
  • 你说得对,我编辑了我的答案以澄清这一点。假设在排序发生之前遍历了整个生成器,但它仍然具有内存方面的优势。
【解决方案2】:

不,您仍在使用sorted() 创建一个全新的列表

output = SomeExpensiveCallEgDatabase()
results = [result[0] for result in output]
results.sort()
return results

会更接近生成器版本。

我认为最好使用生成器版本,因为 Python 的某些未来版本可能能够利用这一点更有效地工作。免费提速总是好的。

【讨论】:

    【解决方案3】:

    是的,你是对的(虽然我相信排序例程仍然被称为 tim-sort,在 uncle timmy 之后)

    【讨论】:

      猜你喜欢
      • 2011-05-08
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 2017-08-05
      • 2020-01-29
      • 1970-01-01
      • 2011-05-30
      • 1970-01-01
      相关资源
      最近更新 更多