简体   繁体   English

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

[英]Python: Remove items from list while iterating, which have non-trivial removal conditions

This is not a dupe of other questions I have found such as: 这不是我发现的其他问题的虚伪,例如:

Remove items from a list while iterating 迭代时从列表中删除项目

Python: Removing list element while iterating over list Python:在遍历列表时删除列表元素

The problem is this: given a list of classes, such as abstracted sockets, what is the most Pythonic way to remove them, if there is a non-trivial way of determining if they should be removed? 问题是这样的:给定一个类列表,例如抽象套接字,如果有一种确定是否应删除它们的非平凡方法,则删除它们的最Pythonic方法是什么?

sockets = [ socket1, socket2, socket3 ] # whatever
for sock in sockets:
    try:
        sock.close()
    except:
        pass
    else:
        remove the socket from the list here!

Cannot use the solution from either link. 无法从任一链接使用解决方案。 The "best" solution I can think of with my limited Python knowledge is to create a new list with only the ones that encountered exceptions appended to it. 用有限的Python知识,我可以想到的“最佳”解决方案是创建一个新列表,其中仅将遇到异常的列表附加到该列表中。

sockets = [ socket1, socket2, socket3 ] # whatever
newsockets = []
for sock in sockets:
    try:
        sock.close()
    except:
        newsockets.append(sock)
sockets = newsockets

This still feels wrong, however. 但是,这仍然感觉不对。 Is there a better way? 有没有更好的办法?

EDIT for moderator who ignored my explicit statement that the question this was marked as a dupe of is not a dupe. 主持人的EDIT忽略了我的明确声明,即该问题被标记为重复对象不是重复对象。

To the first link I posted, you cannot use try/except in a list comprehension. 到我发布的第一个链接,您不能在列表理解中使用try / except。 To the second link (the one it was marked as a dupe of), as the comments say, that is a bad solution. 正如评论所说,到第二个链接(被标记为重复的链接),这是一个不好的解决方案。 remove(non-hashable item or item that doesn't have __eq__) does not work. remove(non-hashable item or item that doesn't have __eq__)不起作用。

Usually the proper way algorithmically is to build a new list, then either replace the original list with the new list, or slice it in there, if you expect to remove a substantial number of the sockets: 通常,算法上正确的方法是建立一个新列表,然后用新列表替换原始列表,或者在其中切片(如果您希望删除大量套接字):

sockets = [socket1, socket2, socket3] # whatever
new_sockets = []
for sock in sockets:
    try:
        sock.close()
    except:
        new_sockets.append(sock)

sockets[:] = new_sockets

The reason for this is that removal of an item that is at an arbitrary location (eg by using sockets.remove ) within the list of n items will have time complexity of O(n) on average, and if you end up removing k items, then the total complexity will be of O(kn) , whereas constructing a new list and replacing the original with new will have time complexity of the scale of O(n) , ie it doesn't depend on the number of sockets removed. 这样做的原因是,删除n项目列表中任意位置的项目(例如,通过使用sockets.remove )将平均具有O(n)时间复杂度,并且如果最终删除k项目,那么总复杂度将为O(kn) ,而构造一个新列表并用new替换原始列表将具有O(n)规模的时间复杂度,即,它不取决于移除的套接字数。


Or, as sockets are hashable, perhaps you should use a set to store them instead. 或者,因为套接字是可散列的,也许您应该使用集合来存储它们。 Then, you need to either construct a new collection from the set to make it possible to iterate and remove at the same time (here I am using a list): 然后,您需要从集合中构造一个新集合,以使其可以同时进行迭代和删除(在这里,我使用的是列表):

sockets = {socket1, socket2, socket3}
for sock in list(sockets):
    try:
        sock.close()
    except: 
        pass
    else:
        sockets.discard(sock)

Creating a new list has O(n) time complexity, but set.discard has only O(1) complexity. 创建新列表的时间复杂度为O(n),但是set.discard仅具有O(1)复杂度。

Another way which gets rid of copying the data structure is to use another set for items that are to be removed : 摆脱复制数据结构的另一种方法是对要删除的项目使用另一组:

sockets = {socket1, socket2, socket3}
to_remove = set()   # create an initially empty set
for sock in sockets:
    try:
        sock.close()
    except: 
        pass
    else:
        to_remove.add(sock)

# remove all sockets that are in to_remove in one 
# operation from sockets.
sockets.difference_update(to_remove)

This has a favourable running time over the other set example in case there are very few if any items to be removed. 如果要删除的项目很少(如果有的话)的话,这比其他示例具有更好的运行时间。

This answer only adds a little explanation as to why you might want to assign the result to a slice of your original list. 该答案仅对为什么要将结果分配给原始列表的一部分提供了一些解释。 @Antti Happala has already given some nice alternatives for potentially improving the runtime. @Antti Happala已经给出了一些不错的选择,可以潜在地改善运行时间。

When you write 当你写

sockets = newsockets

you are assigning the new list to the variable named sockets , whereas if you say 您正在将新列表分配给名为sockets的变量,而如果您说

sockets[:] = newsockets

you are essentially replacing the entries of the same list with the new list. 您实际上是用新列表替换相同列表的条目。

The difference becomes more clear if you previously had another reference to your sockets list like so: 如果您以前像以前这样引用了sockets列表,则区别变得更加明显:

s = sockets

then, after assigning without slicing, s would still point to the old list with none of the sockets removed and sockets would refer to the new list. 然后,在不进行切片s情况下进行分配之后, s仍将指向旧列表,而没有删除任何套接字,而sockets将引用新列表。

Using the slicing version both s and sockets would refer to the same, updated list after removing some elements. 使用切片版本时,删除某些元素后, ssockets都将引用相同的更新列表。

Usually this in-place replacement is what you want, however, there are also times when you might specifically want s and sockets to refer to different versions of the list, in which case you should not assign to a list slice. 通常,此替换是您想要的,但是,有时您可能特别希望ssockets引用列表的不同版本,在这种情况下,您不应该分配给列表片。 It all depends. 一切取决于。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在网络上托管非平凡的python程序? - Hosting a non-trivial python program on the web? Python非平凡的C ++扩展 - Python non-trivial C++ Extension Python记录的非平凡用途? - Python logging for non-trivial uses? 如何在迭代时从列表中删除项目? - How to remove items from a list while iterating? Python:pandas 数据帧上的多处理错误:客户端具有非平凡的 state,它是本地且不可腌制的 - Python: Multiprocessing error on pandas data frame: Clients have non-trivial state that is local and unpickleable 数据流错误:“客户端具有非平凡的本地状态和不可选择的状态” - Dataflow Error: 'Clients have non-trivial state that is local and unpickleable' 使用条件从Python列表中删除项目 - Remove items from a list in Python using conditions 从带有条件的Python列表中删除随机项目 - Remove Random Items from a list in Python with conditions 在迭代列表时,Python不会从列表中删除所有项目 - Python doesn't remove all the items from a list while iterating over the list 有关使用C扩展或Cython优化非平凡Python应用程序的教程 - Tutorials on optimizing non-trivial Python applications with C extensions or Cython
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM