Python:迭代时从列表中删除项目,这些项目具有非平凡的删除条件

时间:2016-12-26 18:22:39

标签: python

这不是我发现的其他问题,例如:

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__)不起作用。

2 个答案:

答案 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将引用新列表。

在删除某些元素后,使用切片版本ssockets将引用相同的更新列表。

通常这种就地替换是您想要的,但是,有时候您可能特别希望ssockets引用列表的不同版本,在这种情况下您应该不分配给列表切片。这一切都取决于。