问题:如何删除列表中的项目(如果存在)?
我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
为什么会发生这种情况,我该如何解决?
I am getting new_tag
from a form text field with self.response.get("new_tag")
and selected_tags
from checkbox fields with
self.response.get_all("selected_tags")
I combine them like this:
tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)
(f1.striplist
is a function that strips white spaces inside the strings in the list.)
But in the case that tag_list
is empty (no new tags are entered) but there are some selected_tags
, new_tag_list
contains an empty string " "
.
For example, from logging.info
:
new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']
How do I get rid of the empty string?
If there is an empty string in the list:
>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']
But if there is no empty string:
>>> 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"
But this gives:
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
Why does this happen, and how do I work around it?
回答 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)
- 足够简单,可能是我的选择。对于小清单(无法抗拒一线)
这种先问后问的态度在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年左右,圭多甚至被谈论下降
filter
-与同伴一起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)
笔记
- 您可能要使用不等式运算符
!=
代替is not
(区别很重要)
- 对于暗示要使用列表副本的方法的批评者:与流行的看法相反,生成器表达式并不总是比列表理解更有效-请在抱怨之前进行剖析
1) Almost-English style:
Test for presence using the in
operator, then apply the remove
method.
if thing in some_list: some_list.remove(thing)
The remove
method will remove only the first occurrence of thing
, in order to remove all occurrences you can use while
instead of if
.
while thing in some_list: some_list.remove(thing)
- Simple enough, probably my choice.for small lists (can’t resist one-liners)
This shoot-first-ask-questions-last attitude is common in Python. Instead of testing in advance if the object is suitable, just carry out the operation and catch relevant Exceptions:
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!")
Off course the second except clause in the example above is not only of questionable humor but totally unnecessary (the point was to illustrate duck-typing for people not familiar with the concept).
If you expect multiple occurrences of thing:
while True:
try:
some_list.remove(thing)
except ValueError:
break
- a little verbose for this specific use case, but very idiomatic in Python.
- this performs better than #1
- PEP 463 proposed a shorter syntax for try/except simple usage that would be handy here, but it was not approved.
However, with contextlib’s suppress() contextmanager (introduced in python 3.4) the above code can be simplified to this:
with suppress(ValueError, AttributeError):
some_list.remove(thing)
Again, if you expect multiple occurrences of thing:
with suppress(ValueError):
while True:
some_list.remove(thing)
3) Functional style:
Around 1993, Python got lambda
, reduce()
, filter()
and map()
, courtesy of a Lisp hacker who missed them and submitted working patches*. You can use filter
to remove elements from the list:
is_not_thing = lambda x: x is not thing
cleaned_list = filter(is_not_thing, some_list)
There is a shortcut that may be useful for your case: if you want to filter out empty items (in fact items where bool(item) == False
, like None
, zero, empty strings or other empty collections), you can pass None as the first argument:
cleaned_list = filter(None, some_list)
- [update]: in Python 2.x,
filter(function, iterable)
used to be equivalent to [item for item in iterable if function(item)]
(or [item for item in iterable if item]
if the first argument is None
); in Python 3.x, it is now equivalent to (item for item in iterable if function(item))
. The subtle difference is that filter used to return a list, now it works like a generator expression – this is OK if you are only iterating over the cleaned list and discarding it, but if you really need a list, you have to enclose the filter()
call with the list()
constructor.
- *These Lispy flavored constructs are considered a little alien in Python. Around 2005, Guido was even talking about dropping
filter
– along with companions map
and reduce
(they are not gone yet but reduce
was moved into the functools module, which is worth a look if you like high order functions).
4) Mathematical style:
List comprehensions became the preferred style for list manipulation in Python since introduced in version 2.0 by PEP 202. The rationale behind it is that List comprehensions provide a more concise way to create lists in situations where map()
and filter()
and/or nested loops would currently be used.
cleaned_list = [ x for x in some_list if x is not thing ]
Generator expressions were introduced in version 2.4 by PEP 289. A generator expression is better for situations where you don’t really need (or want) to have a full list created in memory – like when you just want to iterate over the elements one at a time. If you are only iterating over the list, you can think of a generator expression as a lazy evaluated list comprehension:
for item in (x for x in some_list if x is not thing):
do_your_thing_with(item)
Notes
- you may want to use the inequality operator
!=
instead of is not
(the difference is important)
- for critics of methods implying a list copy: contrary to popular belief, generator expressions are not always more efficient than list comprehensions – please profile before complaining
回答 1
try:
s.remove("")
except ValueError:
print "new_tag_list has no empty string"
请注意,这只会从列表中删除空字符串的一个实例(就像您的代码一样)。您的列表可以包含多个吗?
try:
s.remove("")
except ValueError:
print "new_tag_list has no empty string"
Note that this will only remove one instance of the empty string from your list (as your code would have, too). Can your list contain more than one?
回答 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"
If index
doesn’t find the searched string, it throws the ValueError
you’re seeing. Either
catch the ValueError:
try:
i = s.index("")
del s[i]
except ValueError:
print "new_tag_list has no empty string"
or use find
, which returns -1 in that case.
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是否可以订购数据。
Adding this answer for completeness, though it’s only usable under certain conditions.
If you have very large lists, removing from the end of the list avoids CPython internals having to memmove
, for situations where you can re-order the list. It gives a performance gain to remove from the end of the list, since it won’t need to memmove
every item after the one your removing – back one step (1).
For one-off removals the performance difference may be acceptable, but if you have a large list and need to remove many items – you will likely notice a performance hit.
Although admittedly, in these cases, doing a full list search is likely to be a performance bottleneck too, unless items are mostly at the front of the list.
This method can be used for more efficient removal,
as long as re-ordering the list is acceptable. (2)
def remove_unordered(ls, item):
i = ls.index(item)
ls[-1], ls[i] = ls[i], ls[-1]
ls.pop()
You may want to avoid raising an error when the item
isn’t in the list.
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
- While I tested this with CPython, its quite likely most/all other Python implementations use an array to store lists internally. So unless they use a sophisticated data structure designed for efficient list re-sizing, they likely have the same performance characteristic.
A simple way to test this, compare the speed difference from removing from the front of the list with removing the last element:
python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'
With:
python -m timeit 'a = [0] * 100000' 'while a: a.pop()'
(gives an order of magnitude speed difference where the second example is faster with CPython and PyPy).
- In this case you might consider using a
set
, especially if the list isn’t meant to store duplicates.
In practice though you may need to store mutable data which can’t be added to a set
. Also check on btree’s if the data can be ordered.
回答 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()
以使其首先不返回空字符串。
Eek, don’t do anything that complicated : )
Just filter()
your tags. bool()
returns False
for empty strings, so instead of
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)
you should write
new_tag_list = filter(bool, f1.striplist(tag_string.split(",") + selected_tags))
or better yet, put this logic inside striplist()
so that it doesn’t return empty strings in the first place.
回答 5
这是扔到那里的另一种方法:
next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)
它不会创建列表副本,不会在列表中进行多次遍历,不需要其他异常处理,并且会返回匹配的对象;如果没有匹配项,则返回None。唯一的问题是它需要长篇大论。
通常,当寻找一种不会引发异常的单线解决方案时,next()是必经之路,因为它是少数支持默认参数的Python函数之一。
Here’s another one-liner approach to throw out there:
next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)
It doesn’t create a list copy, doesn’t make multiple passes through the list, doesn’t require additional exception handling, and returns the matched object or None if there isn’t a match. Only issue is that it makes for a long statement.
In general, when looking for a one-liner solution that doesn’t throw exceptions, next() is the way to go, since it’s one of the few Python functions that supports a default argument.
回答 6
您要做的就是这个
list = ["a", "b", "c"]
try:
list.remove("a")
except:
print("meow")
但是这种方法有问题。你必须把东西放在除了那个地方,所以我发现了这一点:
list = ["a", "b", "c"]
if "a" in str(list):
list.remove("a")
All you have to do is this
list = ["a", "b", "c"]
try:
list.remove("a")
except:
print("meow")
but that method has an issue. You have to put something in the except place
so i found this:
list = ["a", "b", "c"]
if "a" in str(list):
list.remove("a")