这不是我发现的其他问题,例如:
Remove items from a list while iterating
Python: Removing list element while iterating over list
问题在于:给定一个类列表,例如抽象套接字,删除它们的最Pythonic方法是什么,如果有一种非平凡的方法来确定它们是否应该被删除?
sockets = [ socket1, socket2, socket3 ] # whatever
for sock in sockets:
try:
sock.close()
except:
pass
else:
remove the socket from the list here!
无法使用任何一个链接中的解决方案。最好的"我能用我有限的Python知识思考的解决方案是创建一个新列表,只列出附加了异常的列表。
sockets = [ socket1, socket2, socket3 ] # whatever
newsockets = []
for sock in sockets:
try:
sock.close()
except:
newsockets.append(sock)
sockets = newsockets
然而,这仍然是错误的。还有更好的方法吗?
主持人的编辑忽略了我的明确陈述,即这个被标记为欺骗的问题不是骗局。
到我发布的第一个链接,你不能在列表理解中使用try / except。
对于第二个链接(标记为欺骗的链接),正如评论所说,这是一个糟糕的解决方案。 remove(non-hashable item or item that doesn't have __eq__)
不起作用。
答案 0 :(得分:3)
通常在算法上正确的方法是构建一个新列表,然后用新列表替换原始列表,或者在那里将其切片,如果你希望删除大量的套接字:
sockets = [socket1, socket2, socket3] # whatever
new_sockets = []
for sock in sockets:
try:
sock.close()
except:
new_sockets.append(sock)
sockets[:] = new_sockets
原因是在sockets.remove
项列表中删除位于任意位置的项目(例如,使用n
)将具有O(n)
的时间复杂度平均值,如果您最终删除k
个项目,则总复杂度将为O(kn)
,而构建新列表并将原始内容替换为新内容将具有{{1}的时间复杂度,即它不依赖于删除的套接字数量。
或者,由于套接字是可以使用的,也许您应该使用一个集来存储它们。然后,您需要从集合中构造一个新集合,以便可以同时进行迭代和删除(这里我使用的是列表):
O(n)
创建新列表的时间复杂度为O(n),但sockets = {socket1, socket2, socket3}
for sock in list(sockets):
try:
sock.close()
except:
pass
else:
sockets.discard(sock)
只有O(1)复杂度。
摆脱复制数据结构的另一种方法是使用另一组来移除 的项目:
set.discard
如果要删除的项目非常少,则其运行时间优于其他设置示例。
答案 1 :(得分:0)
此答案仅添加了一些解释,说明为什么您可能希望将结果分配给原始列表的切片。 @Antti Happala已经提供了一些很好的替代方案,可能会改善运行时。
写作时
sockets = newsockets
您要将新列表分配给名为sockets
的变量,而如果您说
sockets[:] = newsockets
您实际上是用新列表替换相同列表的条目。
如果您之前有另一个对sockets
列表的引用,则差异会变得更加清晰:
s = sockets
然后,在没有切片的情况下进行分配后,s
仍会指向旧列表,没有删除任何套接字,sockets
将引用新列表。
在删除某些元素后,使用切片版本s
和sockets
将引用相同的更新列表。
通常这种就地替换是您想要的,但是,有时候您可能特别希望s
和sockets
引用列表的不同版本,在这种情况下您应该不分配给列表切片。这一切都取决于。