当我在Python中编写代码时,我经常需要根据某些条件从列表或其他序列类型中删除项目.我还没有找到优雅高效的解决方案,因为从当前正在迭代的列表中删除项目是不好的.例如,你不能这样做:
for name in names: if name[-5:] == 'Smith': names.remove(name)
我通常最终做这样的事情:
toremove = [] for name in names: if name[-5:] == 'Smith': toremove.append(name) for name in toremove: names.remove(name) del toremove
这是无效的,相当丑陋和可能有错误(它如何处理多个'John Smith'条目?).有没有人有更优雅的解决方案,或者至少更有效?
那个与词典一起工作的人怎么样?
完成过滤的两种简单方法是:
使用filter
:
names = filter(lambda name: name[-5:] != "Smith", names)
使用列表推导:
names = [name for name in names if name[-5:] != "Smith"]
请注意,这两种情况都保留谓词函数评估的值True
,因此您必须反转逻辑(即您说"保留没有姓氏史密斯的人"而不是"删除姓氏的人")史密斯").
编辑搞笑...两个人分别发布了我建议的两个答案,因为我发布了我的答案.
您还可以在列表上向后迭代:
for name in reversed(names): if name[-5:] == 'Smith': names.remove(name)
这样做的好处是它不会创建新列表(如filter
列表推导)或使用迭代器而不是列表副本(如[:]
).
请注意,尽管在向后迭代时删除元素是安全的,但插入它们有点棘手.
显而易见的答案是约翰和其他几个人给出的答案,即:
>>> names = [name for name in names if name[-5:] != "Smith"] # <-- slower
但是它的缺点是它创建了一个新的列表对象,而不是重用原始对象.我做了一些分析和实验,我提出的最有效的方法是:
>>> names[:] = (name for name in names if name[-5:] != "Smith") # <-- faster
分配给"names [:]"基本上意味着"用以下值替换名称列表的内容".它与仅仅分配名称不同,因为它不会创建新的列表对象.赋值的右侧是生成器表达式(注意使用括号而不是方括号).这将导致Python在列表中进行迭代.
一些快速分析表明,这比列表理解方法快约30%,比过滤方法快约40%.
警告:虽然这个解决方案比明显的解决方案更快,但它更加模糊,并且依赖于更先进的Python技术.如果您使用它,我建议随附评论.在你真正关心这个特定操作的性能的情况下,这可能是值得使用的(无论如何都非常快).(在我使用它的情况下,我正在进行A*光束搜索,并使用它来从搜索光束中删除搜索点.)
使用列表理解
list = [x for x in list if x[-5:] != "smith"]