问题:如何删除列表中的项目(如果存在)?
我new_tag
从一个表单文本字段self.response.get("new_tag")
和selected_tags
复选框字段
self.response.get_all("selected_tags")
我将它们像这样组合:
tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)
(f1.striplist
此函数会在列表中的字符串内去除空格。)
但在这种情况下tag_list
是空的(没有新的标签进入),但也有一些selected_tags
,new_tag_list
包含一个空字符串" "
。
例如,来自logging.info
:
new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']
我如何摆脱空字符串?
如果列表中有一个空字符串:
>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']
但是,如果没有空字符串:
>>> s = [u'Hello', u'Cool', u'Glam']
>>> if s.index(""):
i = s.index("")
del s[i]
else:
print "new_tag_list has no empty string"
但这给出了:
Traceback (most recent call last):
File "<pyshell#30>", line 1, in <module>
if new_tag_list.index(""):
ValueError: list.index(x): x not in list
为什么会发生这种情况,我该如何解决?
回答 0
1)几乎是英式风格:
使用in
操作员测试是否存在,然后应用该remove
方法。
if thing in some_list: some_list.remove(thing)
该remove
方法将仅删除的第一个匹配项thing
,以便删除您可以while
代替使用的所有匹配项if
。
while thing in some_list: some_list.remove(thing)
- 足够简单,可能是我的选择。对于小清单(无法抗拒一线)
2)鸭类型的,EAFP风格:
这种先问后问的态度在Python中很常见。无需预先测试对象是否合适,只需执行操作并捕获相关的Exception:
try:
some_list.remove(thing)
except ValueError:
pass # or scream: thing not in some_list!
except AttributeError:
call_security("some_list not quacking like a list!")
当然,上面示例中的第二个except子句不仅具有可疑的幽默性,而且是完全没有必要的(重点是为不熟悉该概念的人说明鸭子式的用法)。
如果您希望事物发生多次:
while True:
try:
some_list.remove(thing)
except ValueError:
break
- 这个特定用例有点冗长,但在Python中却很惯用。
- 这比#1表现更好
- PEP 463为try / except提出了一种较短的语法,用于简单尝试/exceptions,在这里很方便,但未获批准。
但是,使用contextlib的prevent()contextmanager(在python 3.4中引入),可以将上述代码简化为:
with suppress(ValueError, AttributeError):
some_list.remove(thing)
同样,如果您希望事物发生多次:
with suppress(ValueError):
while True:
some_list.remove(thing)
3)功能风格:
1993年左右,巨蟒有lambda
,reduce()
,filter()
和map()
,一个礼貌的Lisp黑客谁错过了他们,并提交工作补丁*。您可以用来filter
从列表中删除元素:
is_not_thing = lambda x: x is not thing
cleaned_list = filter(is_not_thing, some_list)
有一个对您的情况有用的快捷方式:如果您想过滤出空项目(实际上bool(item) == False
是None
零,空字符串或其他空集合的项目),则可以将None作为第一个参数传递:
cleaned_list = filter(None, some_list)
- [update]:在Python 2.x中,
filter(function, iterable)
曾经等价于[item for item in iterable if function(item)]
(或[item for item in iterable if item]
第一个参数为None
);在Python 3.x中,它现在等效于(item for item in iterable if function(item))
。细微的区别是过滤器用于返回列表,现在它像生成器表达式一样工作-如果仅遍历已清理的列表并将其丢弃,则可以,但是如果您确实需要列表,则必须将filter()
调用括起来与list()
构造函数。 - *这些Lispy风格的构造在Python中被认为有点陌生。2005年左右,
-与同伴一起 map
和reduce
(他们还没有消失,但reduce
被转移到functools模块,这是值得一试,如果你喜欢高阶函数)。
4)数学风格:
自PEP 202在2.0版中引入列表理解以来,列表理解成为Python中列表处理的首选样式。其背后的理由是,列表推导提供了一种更简洁的方法,可以在当前使用map()
和filter()
和/或嵌套循环的情况下创建列表。
cleaned_list = [ x for x in some_list if x is not thing ]
PEP 289在2.4版中引入了生成器表达式。生成器表达式更适合您不需要(或不想)在内存中创建完整列表的情况,例如您只想一次遍历一个元素的情况。如果仅遍历列表,则可以将生成器表达式视为延迟的求值列表理解:
for item in (x for x in some_list if x is not thing):
do_your_thing_with(item)
- 请参阅GvR撰写的此Python历史博客文章。
- 此语法的灵感来自于数学中的集构建器符号。
- Python 3还设置并决定了理解能力。
笔记
- 您可能要使用不等式运算符
!=
代替is not
(区别很重要) - 对于暗示要使用列表副本的方法的批评者:与流行的看法相反,生成器表达式并不总是比列表理解更有效-请在抱怨之前进行剖析
回答 1
try:
s.remove("")
except ValueError:
print "new_tag_list has no empty string"
请注意,这只会从列表中删除空字符串的一个实例(就像您的代码一样)。您的列表可以包含多个吗?
回答 2
如果index
找不到搜索到的字符串,则会抛出ValueError
您所看到的字符串。要么捕获ValueError:
try:
i = s.index("")
del s[i]
except ValueError:
print "new_tag_list has no empty string"
或 use find
,在这种情况下返回-1。
i = s.find("")
if i >= 0:
del s[i]
else:
print "new_tag_list has no empty string"
回答 3
添加此答案以提高完整性,尽管它仅在某些条件下可用。
如果列表非常大,则从列表末尾删除可避免CPython内部结构必须使用memmove
,因为您可以对列表重新排序。从列表的末尾删除它可以提高性能,因为它不需要删除后一个步骤(1)之后的memmove
每个项目。
对于一次性删除,性能差异是可以接受的,但是如果列表很大并且需要删除很多项目,则可能会发现性能下降。
尽管可以肯定的是,在这些情况下,进行完整列表搜索也可能是性能瓶颈,除非项目大多位于列表的最前面。
只要可以重新排序列表,此方法就可以用于更有效的删除。(2)
def remove_unordered(ls, item):
i = ls.index(item)
ls[-1], ls[i] = ls[i], ls[-1]
ls.pop()
当item
列表中没有时,您可能要避免引发错误。
def remove_unordered_test(ls, item):
try:
i = ls.index(item)
except ValueError:
return False
ls[-1], ls[i] = ls[i], ls[-1]
ls.pop()
return True
- 当我使用CPython进行测试时,它很可能大多数/所有其他Python实现都使用数组在内部存储列表。因此,除非他们使用设计用于有效调整列表大小的复杂数据结构,否则它们可能具有相同的性能特征。
一种简单的测试方法,比较从列表的最前面移除到最后一个元素所带来的速度差异:
python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'
带有:
python -m timeit 'a = [0] * 100000' 'while a: a.pop()'
(给出一个数量级的速度差,其中第二个示例使用CPython和PyPy更快)。
- 在这种情况下,您可以考虑使用
set
,尤其是在列表不打算存储重复项的情况下。
在实践中,尽管您可能需要存储无法添加到的可变数据set
。同时检查btree是否可以订购数据。
回答 4
哎呀,不要做任何复杂的事情:)
只是filter()
您的标签。 bool()
返回False
空字符串,所以代替
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)
你应该写
new_tag_list = filter(bool, f1.striplist(tag_string.split(",") + selected_tags))
或更好的方法是,将此逻辑放入内部,striplist()
以使其首先不返回空字符串。
回答 5
这是扔到那里的另一种方法:
next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)
它不会创建列表副本,不会在列表中进行多次遍历,不需要其他异常处理,并且会返回匹配的对象;如果没有匹配项,则返回None。唯一的问题是它需要长篇大论。
通常,当寻找一种不会引发异常的单线解决方案时,next()是必经之路,因为它是少数支持默认参数的Python函数之一。
回答 6
您要做的就是这个
list = ["a", "b", "c"]
try:
list.remove("a")
except:
print("meow")
但是这种方法有问题。你必须把东西放在除了那个地方,所以我发现了这一点:
list = ["a", "b", "c"]
if "a" in str(list):
list.remove("a")