Python片分配内存使用率

Python片分配内存使用率,python,memory-management,performance,benchmarking,Python,Memory Management,Performance,Benchmarking,我在这里读到一条关于堆栈溢出的评论,当更改列表时,执行片分配更有效。比如说, a[:] = [i + 6 for i in a] 应该比 a = [i + 6 for i in a] 因为前者替换现有列表中的元素,而后者创建一个新列表并将a重新绑定到该新列表中,将旧的a保留在内存中,直到可以对其进行垃圾收集。将两者作为速度基准,后者稍快一些: $ python -mtimeit -s 'a = [1, 2, 3]' 'a[:] = [i + 6 for i in a]' 1000000 lo

我在这里读到一条关于堆栈溢出的评论,当更改列表时,执行片分配更有效。比如说,

a[:] = [i + 6 for i in a]
应该比

a = [i + 6 for i in a]
因为前者替换现有列表中的元素,而后者创建一个新列表并将
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
这就是我所期望的,因为重新绑定变量应该比替换列表中的元素更快。然而,我找不到任何支持内存使用声明的官方文档,我也不确定如何对其进行基准测试

从表面上看,内存使用声明对我来说是有意义的。但是,再仔细考虑一下,我希望在前一种方法中,解释器将根据列表理解创建一个新列表,然后将该列表中的值复制到
a
,使匿名列表四处浮动,直到垃圾回收为止。如果是这样,那么前一种方法将使用相同数量的内存,同时速度也会变慢

有人能明确地(通过基准测试或官方文档)说明这两种方法中哪一种更节省内存/哪一种是首选方法吗

提前谢谢。

电话线

a[:] = [i + 6 for i in a]
v_as_SF = PySequence_Fast(v, "can only assign an iterable");
不会保存任何内存。Python确实会首先计算右侧,如中所述:

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

在本例中,单个结果对象将是一个新列表,目标列表中的单个目标将是
a[:]

我们可以用生成器表达式替换列表理解:

a[:] = (i + 6 for i in a)
现在,右侧的计算结果是生成器,而不是列表。基准测试表明,这仍然比幼稚测试慢

a = [i + 6 for i in a]
那么生成器表达式真的节省了内存吗?乍一看,你可能认为是这样。但深入研究表明,事实并非如此。线路

v_as_SF = PySequence_Fast(v, "can only assign an iterable");
用于首先将iterable(在本例中为生成器)转换为元组,然后将元组复制到旧列表中。元组使用的内存量与列表相同,因此在本例中,使用生成器表达式与使用列表理解基本相同。在最后一次复制期间,将重用原始列表中的项目


寓意似乎是,最简单的方法在任何方面都是最好的。

+1无情地粉碎了过早的(内存)优化程序。感谢您提供详细而深刻的答案!对于上面的评论,我想补充一点,如果您处理的是一个包含500万个元素的列表,并且可以在复制和不复制之间进行选择,那么这可能不是过早的优化。:)@米奇:如果你有500万个条目,那么你最好使用NumPy数组,而不是Python列表。看看引擎盖下的机制,这是一个非常好和明确的答案+1性能方面可能值得考虑,但我认为您更有可能遇到实际情况(在较大的程序中),在这种情况下,您会传递对列表的引用,例如从Class1到Class2。在第一个实例中,使用slice赋值修改Class1的列表将保留Class2的引用。在您引用的第二个例子中,修改Class1的列表意味着Class2将保留对不再有效的列表的引用。@Brandon:这也是事实,我可能应该在我的问题中提到区别。谢谢你的意见。