Vervang waarden in lijst met Python

Ik heb een lijst waarin ik waarden wil vervangen door Geen, waarbij condition() True retourneert.

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

Als de voorwaarde bijvoorbeeld wordt gecontroleerd, moet bool(item%2) het volgende retourneren:

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

Wat is de meest efficiënte manier om dit te doen?


Antwoord 1, autoriteit 100%

Bouw een nieuwe lijst met een lijstbegrip:

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

Je kunt de originele lijst desgewenst ter plekke aanpassen, maar dit bespaart geen tijd:

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

Hier zijn (Python 3.6.3) timings die de non-timesave aantonen:

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)

En 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

Antwoord 2, autoriteit 34%

ls = [x if (condition) else None for x in ls]

Antwoord 3, autoriteit 5%

Riffing op een nevenvraag gesteld door de OP in een opmerking, d.w.z.:

wat als ik een generator had die opbrengt?
de waarden van range(11) in plaats van a
lijst. Zou het mogelijk zijn om te vervangen?
waarden in de generator?

Natuurlijk, het is triviaal eenvoudig…:

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

Geef gewoon elke iterabele (inclusief het resultaat van het aanroepen van een generator) door als eerste arg, het predikaat om te beslissen of een waarde moet worden vervangen als tweede arg, en laat ‘er rippen.

Bijvoorbeeld:

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

Antwoord 4, autoriteit 5%

Hier is een andere manier:

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

Antwoord 5

>>> 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]

Antwoord 6

Als u waarden op hun plaats wilt vervangen, kunt u:
update je originele lijst met waarden uit een lijst
begrijpen door toe te wijzen aan het hele stuk van het origineel.

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

Als je meerdere namen hebt die naar de originele lijst verwijzen,
u schreef bijvoorbeeld data2=datavoordat u de lijst veranderde
en u slaat de plaknotatie over voor het toewijzen aan data,
datazal opnieuw binden om naar de nieuw gemaakte lijst te verwijzen, terwijl data2nog steeds naar de oorspronkelijke ongewijzigde lijst verwijst.

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]

Opmerking: dit is geen aanbeveling om over het algemeen de ene boven de andere te verkiezen
(lijst op zijn plaats wijzigen of niet), maar gedrag waar u zich bewust van moet zijn.


Antwoord 7

Dit kan helpen…

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

Other episodes