Python:在迭代时从列表中删除项,这些项具有非平凡的删除条件
这不是我发现的其他问题的重复,例如: 问题是:给定一个类列表,比如抽象套接字,如果有一种非常简单的方法来确定是否应该删除它们,那么删除它们最适合的方法是什么Python:在迭代时从列表中删除项,这些项具有非平凡的删除条件,python,Python,这不是我发现的其他问题的重复,例如: 问题是:给定一个类列表,比如抽象套接字,如果有一种非常简单的方法来确定是否应该删除它们,那么删除它们最适合的方法是什么 sockets = [ socket1, socket2, socket3 ] # whatever for sock in sockets: try: sock.close() except: pass else: remove the socket from t
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。
至于第二个链接(它被标记为复制的链接),正如评论所说,这是一个糟糕的解决方案<代码>删除(非散列项或没有uuu eq uuu的项)不起作用。通常正确的算法是建立一个新列表,然后用新列表替换原始列表,或者在其中切片,如果您希望删除大量套接字:
sockets = [socket1, socket2, socket3] # whatever
new_sockets = []
for sock in sockets:
try:
sock.close()
except:
new_sockets.append(sock)
sockets[:] = new_sockets
原因是,移除n
项列表中任意位置的项(例如,使用sockets.remove
)平均时间复杂度为O(n)
,如果最终移除k
项,则总复杂度为O(kn)
,然而,构建一个新的列表并用新的替换原来的列表将具有O(n)
规模的时间复杂性,即它不依赖于移除的套接字的数量
或者,由于套接字是可散列的,也许您应该使用一个集合来存储它们。然后,您需要从集合中构造一个新集合,以便可以同时迭代和删除(这里我使用的是一个列表): 创建新列表的时间复杂度为O(n),但
set.discard
的时间复杂度仅为O(1)
避免复制数据结构的另一种方法是对要删除的项使用另一个集合:
如果要删除的项目非常少,则与其他设置示例相比,此设置具有良好的运行时间。通常正确的算法方法是构建一个新列表,然后用新列表替换原始列表,或者在其中切片,如果您希望删除大量套接字:
sockets = [socket1, socket2, socket3] # whatever
new_sockets = []
for sock in sockets:
try:
sock.close()
except:
new_sockets.append(sock)
sockets[:] = new_sockets
原因是,移除n
项列表中任意位置的项(例如,使用sockets.remove
)平均时间复杂度为O(n)
,如果最终移除k
项,则总复杂度为O(kn)
,然而,构建一个新的列表并用新的替换原来的列表将具有O(n)
规模的时间复杂性,即它不依赖于移除的套接字的数量
或者,由于套接字是可散列的,也许您应该使用一个集合来存储它们。然后,您需要从集合中构造一个新集合,以便可以同时迭代和删除(这里我使用的是一个列表): 创建新列表的时间复杂度为O(n),但
set.discard
的时间复杂度仅为O(1)
避免复制数据结构的另一种方法是对要删除的项使用另一个集合:
如果要删除的项目很少(如果有的话),此示例的运行时间比其他示例的运行时间要长。此答案只添加了一点解释,说明了为什么要将结果分配到原始列表的某个部分@Antti Happala已经给出了一些很好的备选方案,可以潜在地改进运行时
当你写作时
sockets = newsockets
您正在将新列表分配给名为sockets
的变量
sockets[:] = newsockets
实际上,您正在用新列表替换相同列表的条目
如果您以前对sockets
列表有其他引用,则差异会变得更加明显,如下所示:
s = sockets
然后,在没有切片的情况下分配后,s
仍将指向没有移除任何套接字的旧列表,sockets
将引用新列表
使用切片版本,s
和sockets
将在删除某些元素后引用相同的更新列表
通常,这种就地替换是您想要的,但是,有时您可能特别希望s
和sockets
引用列表的不同版本,在这种情况下,您不应该指定给列表片段。这要看情况而定。这个答案只是补充了一点解释,说明了为什么要将结果分配给原始列表的一部分@Antti Happala已经给出了一些很好的备选方案,可以潜在地改进运行时
当你写作时
sockets = newsockets
您正在将新列表分配给名为sockets
的变量
sockets[:] = newsockets
实际上,您正在用新列表替换相同列表的条目
如果您以前对sockets
列表有其他引用,则差异会变得更加明显,如下所示:
s = sockets
然后,在没有切片的情况下分配后,s
仍将指向没有移除任何套接字的旧列表,sockets
将引用新列表
使用切片版本,s
和sockets
将在删除某些元素后引用相同的更新列表
通常,这种就地替换是您想要的,但是,有时您可能特别希望s
和sockets
引用列表的不同版本,在这种情况下,您不应该这样做