ChatGPT解决这个技术问题 Extra ChatGPT

Replace values in list using Python [duplicate]

This question already has answers here: Finding and replacing elements in a list (10 answers) Closed 3 years ago.

I have a list where I want to replace values with None where condition() returns True.

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

For example, if condition checks bool(item%2) should return:

[None, 1, None, 3, None, 5, None, 7, None, 9, None]

What is the most efficient way to do this?

use the itertools module, it's the most efficient.
For in-place replacement comparisons, take a look at this answer
Wow. This question(2009) already has answers from the *future*(2010). So, the important question: How can I time travel? :)

u
user2357112

Build a new list with a list comprehension:

new_items = [x if x % 2 else None for x in items]

You can modify the original list in-place if you want, but it doesn't actually save time:

items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for index, item in enumerate(items):
    if not (item % 2):
        items[index] = None

Here are (Python 3.6.3) timings demonstrating the non-timesave:

In [1]: %%timeit
   ...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
   ...: for index, item in enumerate(items):
   ...:     if not (item % 2):
   ...:         items[index] = None
   ...:
1.06 µs ± 33.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [2]: %%timeit
   ...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
   ...: new_items = [x if x % 2 else None for x in items]
   ...:
891 ns ± 13.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

And Python 2.7.6 timings:

In [1]: %%timeit
   ...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
   ...: for index, item in enumerate(items):
   ...:     if not (item % 2):
   ...:         items[index] = None
   ...: 
1000000 loops, best of 3: 1.27 µs per loop
In [2]: %%timeit
   ...: items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
   ...: new_items = [x if x % 2 else None for x in items]
   ...: 
1000000 loops, best of 3: 1.14 µs per loop

is that the most efficient? doesn't enumerate have to create an iterator and form a tuple, adding overhead? are lists in python arraylists, giving you constant time access?
I think, and I might be wrong, that he meant for a copy of the list to be returned instead of modifying the original in place. But still, +1 for offering the efficient solution when in-place modification is allowed.
@geowa4: Python "lists" are actually arrays. enumerate() will ad a small overhead, but if that's unacceptable the index can be tracked manually. @ak: I don't understand the question. imap() is not an in-place operation.
Thanks for the answers. But isn't there enough of a use case here for python lists to have a replace method? (similar to str.replace). Something that avoids the need for this: def replace(items, a, b): return [b if x == a else x for x in items]
I think you got "easiest to read" and "most efficient" backwards
L
Laurence Gonsalves
ls = [x if (condition) else None for x in ls]

b
balpha

Here's another way:

>>> L = range (11)
>>> map(lambda x: x if x%2 else None, L)
[None, 1, None, 3, None, 5, None, 7, None, 9, None]

@gath: Don't aspire to write one-liners for every purpose. Sometimes, they increase readability or performance, but often they don't. As to hints: Get to know the tools that Python ofters, especially list (and for Python 3 also dict) comprehensions, the ternary operator, anonymous (lambda) functions, and functions like map, zip, filter, reduce, etc.
A
Alex Martelli

Riffing on a side question asked by the OP in a comment, i.e.:

what if I had a generator that yields the values from range(11) instead of a list. Would it be possible to replace values in the generator?

Sure, it's trivially easy...:

def replaceiniter(it, predicate, replacement=None):
  for item in it:
    if predicate(item): yield replacement
    else: yield item

Just pass any iterable (including the result of calling a generator) as the first arg, the predicate to decide if a value must be replaced as the second arg, and let 'er rip.

For example:

>>> list(replaceiniter(xrange(11), lambda x: x%2))
[0, None, 2, None, 4, None, 6, None, 8, None, 10]

+1 hehe... i want to learn how to write this "one" line nifty python solution... hint pls
@gath, I don't understand your question -- comments are pretty limiting so you should open a new question so you can expand and clarify what is it you're looking for...
e
eduffy
>>> L = range (11)
>>> [ x if x%2 == 1 else None for x in L ]
[None, 1, None, 3, None, 5, None, 7, None, 9, None]

D
Darkonaut

In case you want to replace values in place, you can update your original list with values from a list comprehension by assigning to the whole slice of the original.

data = [*range(11)] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
id_before = id(data)
data[:] = [x if x % 2 else None for x in data]
data
# Out: [None, 1, None, 3, None, 5, None, 7, None, 9, None]
id_before == id(data)  # check if list is still the same
# Out: True

If you have multiple names pointing to the original list, for example you wrote data2=data before changing the list and you skip the slice notation for assigning to data, data will rebind to point to the newly created list while data2 still points to the original unchanged list.

data = [*range(11)] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
data2 = data
id_before = id(data)
data = [x if x % 2 else None for x in data]  # no [:] here
data
# Out: [None, 1, None, 3, None, 5, None, 7, None, 9, None]
id_before == id(data)  # check if list is still the same
# Out: False
data2
# Out: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Note: This is no recommendation for generally preferring one over the other (changing list in place or not), but behavior you should be aware of.


E
Emil

This might help...

test_list = [5, 8]
test_list[0] = None
print test_list
#prints [None, 8]

Can you explain some about why you think it might help?
@T-Heron It could be modified to fulfill what the question is asking for
If it needs to be modified, then it isn't an answer to the question that is being asked. Please either make (or explain) the necessary modifications yourself, or delete the answer.
This is hard coding , i don't think best way to do it .
This is not useful in a real usage scenario.