How to delete an item in a list if it exists?
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)
2) Duck-typed, EAFP style:
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 thingcleaned_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 isNone
); 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 thefilter()
call with thelist()
constructor. - *These Lispy flavored constructs are considered a little alien in Python. Around 2005, Guido was even talking about dropping
filter
- along with companionsmap
andreduce
(they are not gone yet butreduce
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)
- See this Python history blog post by GvR.
- This syntax is inspired by the set-builder notation in math.
- Python 3 has also set and dict comprehensions.
Notes
- you may want to use the inequality operator
!=
instead ofis 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
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"