Python - 列表理解中的 append() 操作导致内存气球
Python - append() operation in list comprehension resulting in memory balloon
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
2 GB RAM, 4 GB Swap
----
import re
fieldname_groups = ("(ip,clienthost,ip_addr)", "(username,user,id)")
srcgroup = [x.strip() for x in re.sub('[\(\)]+', '', fieldname_groups[0]).split(',')]
dstgroup = [x.strip() for x in re.sub('[\(\)]+', '', fieldname_groups[1]).split(',')]
if not srcgroup or not dstgroup: raise Exception("No srcgroup or dstgroup specified!")
# srcgroup is now ['ip', 'clienthost', 'ip_addr']
# dstgroup is now ['username', 'user', 'id']
# Now append the string '.keyword' to a copy of every string in each list
[srcgroup.append('%s.keyword' % x) for x in srcgroup]
[dstgroup.append('%s.keyword' % x) for x in dstgroup]
# srcgroup should now be ['ip', 'clienthost', 'ip_addr', 'ip.keyword', 'clienthost.keyword', 'ip_addr.keyword']
# dstgroup should be similar
每次我 运行 这段代码,一旦我点击列表理解,内存就会膨胀并且进程被终止。
我不明白这里的问题是什么;我觉得这是我一直在做的事情,但这次它不起作用,所以这可能是一个业余错误,但我很感激任何帮助来解决它。我什至尝试重写代码以使用标准的 for 循环,但它仍然爆炸。
谢谢!
正如@Andrej Kesely 所说,您在迭代 时向正在迭代的列表添加元素。因此,您不断向循环中添加元素,因此循环永远不会结束。
我不确定你到底想要什么,但也许你应该做的是动态创建一个新列表来迭代它,而不是你要添加元素的列表,这样:
[srcgroup.append('%s.keyword' % x) for x in srcgroup[:]] # Notice the [:]
[dstgroup.append('%s.keyword' % x) for x in dstgroup[:]] # Notice the [:]
在列表名称的末尾添加 [:] 切片,保留所有元素,并即时将其保存到新对象中,因此不会发生错误。
你正在添加一个列表,同时你正在迭代它 -> 内存将无限增长:
[srcgroup.append('%s.keyword' % x) for x in srcgroup]
[dstgroup.append('%s.keyword' % x) for x in dstgroup]
解决方案是迭代一个副本:
[srcgroup.append('%s.keyword' % x) for x in srcgroup[:]]
[dstgroup.append('%s.keyword' % x) for x in dstgroup[:]]
编辑:
更好的办法是稍微简化一下(根据以下答案):
srcgroup = ['ip', 'keyword']
srcgroup.extend([f'{x}.keyword' for x in srcgroup])
print(srcgroup)
将输出:
['ip', 'keyword', 'ip.keyword', 'keyword.keyword']
在列表理解中,您应该有副作用(例如将项目附加到列表)。列表理解创建一个新列表,您不需要手动 append
项。
如果要替换列表中的每个元素,可以创建一个新列表并替换旧列表:
scrgroup = [('%s.keyword' % x) for x in srcgroup]
dstgroup = [('%s.keyword' % x) for x in dstgroup]
但是,如果您想将具有该格式的每个元素的副本添加到列表中,则需要对其进行扩展:
scrgroup.extend([('%s.keyword' % x) for x in srcgroup])
dstgroup.extend([('%s.keyword' % x) for x in dstgroup])
但是,为了减少内存使用,您可以在这种情况下使用生成器:
scrgroup.extend((('%s.keyword' % x) for x in srcgroup))
dstgroup.extend((('%s.keyword' % x) for x in dstgroup))
扩展列表的正确方法是使用 extend
方法。
srcgroup = [x.strip() for x in re.sub('[\(\)]+', '', fieldname_groups[0]).split(',')]
srcgroup.extend(['%s.keyword' % x) for x in srcgroup])
请注意,extend
的参数是一个新列表,而不是在向 srcgroup
添加新元素时迭代 srcgroup
的生成器表达式,这一点很重要。
在任何情况下,您都不应仅出于表达式的副作用而使用列表理解。使用
for x in ...:
...
而不是
[... for x in ...]
如上所述,您的问题是您要附加到正在迭代的集合。既然如此,复制整个列表就有些浪费了。为避免创建列表的另一个副本,您可以这样做:
srcgroup.extend( ['%s.keyword' % x for x in srcgroup] )
dstgroup.extend( ['%s.keyword' % x for x in dstgroup] )
这将就地修改列表。这是性能比较:
timeit.timeit('arr = range(1000); arr = arr.extend( [x + 1 for x in arr] )')
53.47952483119644
timeit.timeit('arr = range(1000); arr = [arr.append(x + 1) for x in arr[:]]')
118.02281077109734
如您所见,我的建议只用了一半的时间(尽管它们仍然是 O(n))。
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
2 GB RAM, 4 GB Swap
----
import re
fieldname_groups = ("(ip,clienthost,ip_addr)", "(username,user,id)")
srcgroup = [x.strip() for x in re.sub('[\(\)]+', '', fieldname_groups[0]).split(',')]
dstgroup = [x.strip() for x in re.sub('[\(\)]+', '', fieldname_groups[1]).split(',')]
if not srcgroup or not dstgroup: raise Exception("No srcgroup or dstgroup specified!")
# srcgroup is now ['ip', 'clienthost', 'ip_addr']
# dstgroup is now ['username', 'user', 'id']
# Now append the string '.keyword' to a copy of every string in each list
[srcgroup.append('%s.keyword' % x) for x in srcgroup]
[dstgroup.append('%s.keyword' % x) for x in dstgroup]
# srcgroup should now be ['ip', 'clienthost', 'ip_addr', 'ip.keyword', 'clienthost.keyword', 'ip_addr.keyword']
# dstgroup should be similar
每次我 运行 这段代码,一旦我点击列表理解,内存就会膨胀并且进程被终止。
我不明白这里的问题是什么;我觉得这是我一直在做的事情,但这次它不起作用,所以这可能是一个业余错误,但我很感激任何帮助来解决它。我什至尝试重写代码以使用标准的 for 循环,但它仍然爆炸。
谢谢!
正如@Andrej Kesely 所说,您在迭代 时向正在迭代的列表添加元素。因此,您不断向循环中添加元素,因此循环永远不会结束。
我不确定你到底想要什么,但也许你应该做的是动态创建一个新列表来迭代它,而不是你要添加元素的列表,这样:
[srcgroup.append('%s.keyword' % x) for x in srcgroup[:]] # Notice the [:]
[dstgroup.append('%s.keyword' % x) for x in dstgroup[:]] # Notice the [:]
在列表名称的末尾添加 [:] 切片,保留所有元素,并即时将其保存到新对象中,因此不会发生错误。
你正在添加一个列表,同时你正在迭代它 -> 内存将无限增长:
[srcgroup.append('%s.keyword' % x) for x in srcgroup]
[dstgroup.append('%s.keyword' % x) for x in dstgroup]
解决方案是迭代一个副本:
[srcgroup.append('%s.keyword' % x) for x in srcgroup[:]]
[dstgroup.append('%s.keyword' % x) for x in dstgroup[:]]
编辑:
更好的办法是稍微简化一下(根据以下答案):
srcgroup = ['ip', 'keyword']
srcgroup.extend([f'{x}.keyword' for x in srcgroup])
print(srcgroup)
将输出:
['ip', 'keyword', 'ip.keyword', 'keyword.keyword']
在列表理解中,您应该有副作用(例如将项目附加到列表)。列表理解创建一个新列表,您不需要手动 append
项。
如果要替换列表中的每个元素,可以创建一个新列表并替换旧列表:
scrgroup = [('%s.keyword' % x) for x in srcgroup]
dstgroup = [('%s.keyword' % x) for x in dstgroup]
但是,如果您想将具有该格式的每个元素的副本添加到列表中,则需要对其进行扩展:
scrgroup.extend([('%s.keyword' % x) for x in srcgroup])
dstgroup.extend([('%s.keyword' % x) for x in dstgroup])
但是,为了减少内存使用,您可以在这种情况下使用生成器:
scrgroup.extend((('%s.keyword' % x) for x in srcgroup))
dstgroup.extend((('%s.keyword' % x) for x in dstgroup))
扩展列表的正确方法是使用 extend
方法。
srcgroup = [x.strip() for x in re.sub('[\(\)]+', '', fieldname_groups[0]).split(',')]
srcgroup.extend(['%s.keyword' % x) for x in srcgroup])
请注意,extend
的参数是一个新列表,而不是在向 srcgroup
添加新元素时迭代 srcgroup
的生成器表达式,这一点很重要。
在任何情况下,您都不应仅出于表达式的副作用而使用列表理解。使用
for x in ...:
...
而不是
[... for x in ...]
如上所述,您的问题是您要附加到正在迭代的集合。既然如此,复制整个列表就有些浪费了。为避免创建列表的另一个副本,您可以这样做:
srcgroup.extend( ['%s.keyword' % x for x in srcgroup] )
dstgroup.extend( ['%s.keyword' % x for x in dstgroup] )
这将就地修改列表。这是性能比较:
timeit.timeit('arr = range(1000); arr = arr.extend( [x + 1 for x in arr] )')
53.47952483119644
timeit.timeit('arr = range(1000); arr = [arr.append(x + 1) for x in arr[:]]')
118.02281077109734
如您所见,我的建议只用了一半的时间(尽管它们仍然是 O(n))。