Python切片分配内存使用情况

Mit*_*ren 22 python performance benchmarking memory-management

我在这里读了一篇关于Stack Overflow的评论,在更改列表时进行切片分配的内存效率更高.例如,

a[:] = [i + 6 for i in a]
Run Code Online (Sandbox Code Playgroud)

应该比内存更有效

a = [i + 6 for i in a]
Run Code Online (Sandbox Code Playgroud)

因为前者替换现有列表中的元素,而后者创建一个新列表并重新绑定a到该新列表,将旧内容留a在内存中,直到它可以被垃圾收集.对速度进行基准测试,后者稍快一些:

$ python -mtimeit -s 'a = [1, 2, 3]' 'a[:] = [i + 6 for i in a]'
1000000 loops, best of 3: 1.53 usec per loop
$ python -mtimeit -s 'a = [1, 2, 3]' 'a = [i + 6 for i in a]'
1000000 loops, best of 3: 1.37 usec per loop
Run Code Online (Sandbox Code Playgroud)

这就是我所期望的,因为重新绑定变量应该比替换列表中的元素更快.但是,我找不到任何支持内存使用声明的官方文档,而且我不确定如何对其进行基准测试.

从表面上看,内存使用声明对我来说很有意义.但是,考虑一下,我希望在前一种方法中,解释器会从列表推导中创建一个新列表,然后将该列表中的值复制到a,让匿名列表浮动,直到它被垃圾收集.如果是这种情况,那么前一种方法将使用相同数量的内存,同时也会更慢.

任何人都可以明确地(使用基准或官方文档)显示两种方法中的哪一种更具内存效率/哪种是首选方法?

提前致谢.

Sve*_*ach 45

这条线

a[:] = [i + 6 for i in a]
Run Code Online (Sandbox Code Playgroud)

不会保存任何记忆.Python确实首先评估右侧,如语言文档中所述:

赋值语句计算表达式列表(请记住,这可以是单个表达式或以逗号分隔的列表,后者产生元组)并从左到右将单个结果对象分配给每个目标列表.

在目前的情况下,单个结果对象将是新列表,并且目标列表中的单个目标将是a[:].

我们可以用生成器表达式替换列表推导:

a[:] = (i + 6 for i in a)
Run Code Online (Sandbox Code Playgroud)

现在,右侧评估生成器而不是列表.基准测试表明,这仍然比天真的要慢

a = [i + 6 for i in a]
Run Code Online (Sandbox Code Playgroud)

那么生成器表达式实际上是否会保存任何内存 乍一看,你可能会认为它确实如此.但深入研究函数list_ass_slice()源代码表明它没有.这条线

v_as_SF = PySequence_Fast(v, "can only assign an iterable");
Run Code Online (Sandbox Code Playgroud)

使用PySequence_Fast()将iterable(在本例中为生成器)转换为元组,然后将其复制到旧列表中.元组使用与列表相同的内存量,因此使用生成器表达式与在这种情况下使用列表推导基本相同.在最后一个副本期间,重复使用原始列表的项目.

道德似乎是最简单的方法在任何方面都是最好的方法.

  • +1对于过早(记忆)优化者的无情粉碎. (5认同)
  • 感谢您提供详尽而有见地的答案!作为对以上评论者的回应,我想补充一点,如果您要处理500万个元素的列表,并且可以在复制和不复制之间进行选择,则这可能不是过早的优化。:) (2认同)
  • @Mitch:如果您有 500 万个条目,那么使用 NumPy 数组可能比使用 Python 列表更好。 (2认同)