我在此处的注释中读到,更改列表时执行切片分配具有更高的内存效率。例如,
a[:] = [i + 6 for i in a]
应该比
a = [i + 6 for i in a]
因为前者会替换现有列表中的元素,而后者会创建一个新列表并重新绑定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,从而使匿名列表一直浮动直到被垃圾回收为止。 。如果真是这样,那么前一种方法将使用相同数量的内存,同时速度也会变慢。
谁能(用基准或官方文档)明确显示这两种方法中哪一种更有效地使用内存/哪一种是首选方法?
提前致谢。
线
不会节省任何内存。如语言文档中所述,Python会首先评估右侧:
赋值语句评估表达式列表(请记住,它可以是单个表达式或逗号分隔的列表,后者产生一个元组),并将单个结果对象从左到右分配给每个目标列表。
在当前情况下,单个结果对象将是一个新列表,而目标列表中的单个目标将是a[:]。
a[:]
我们可以用生成器表达式代替列表推导:
a[:] = (i + 6 for i in a)
现在,右侧将求值为生成器,而不是列表。基准测试表明,这仍然比天真慢
那么生成器表达式实际上可以节省任何内存吗?乍一看,您可能会认为确实如此。但是深入研究该函数list_ass_slice()的源代码表明事实并非如此。线
list_ass_slice()
v_as_SF = PySequence_Fast(v, "can only assign an iterable");
使用PySequence_Fast()首先将可迭代对象(在这种情况下为生成器)转换为元组,然后将其复制到旧列表中。元组使用与列表相同的内存量,因此在这种情况下,使用生成器表达式与使用列表推导基本上相同。在最后一次复制期间,原始列表的项目被重用。
道德似乎是,无论如何,最简单的方法都是最好的方法。