2013-01-16 10 views
7

Mam listę: mylist = [0, 0, 0, 0, 0]Zastępowanie wybrane elementy listy w Pythonie

Chcę tylko, aby zastąpić wybrane elementy, mówią, że pierwszy, drugi i czwarty przez wspólny numer, A = 100.

Jednym ze sposobów, aby to zrobić:

mylist[:2] = [A]*2 
mylist[3] = A 
mylist 
[100, 100, 0, 100, 0] 

szukam dla jednej liniowej lub łatwiejszy sposób to zrobić. Bardziej ogólna i elastyczna odpowiedź jest lepsza.

+0

Jak chodzi? mylist [: 2], mylist [3] = [A] * 2, A –

Odpowiedz

7

Zwłaszcza, że ​​jesteś zastępując spory kawał list, to bym to zrobić niezmiennie:

mylist = [100 if i in (0, 1, 3) else e for i, e in enumerate(mylist)] 

to zamierzone w Pythonie, który czyni nową list jest jeden-liner, a mutowania się list wymaga jawnej pętli. Zwykle, jeśli nie wiesz, który z nich chcesz, chcesz nowy list. (W niektórych przypadkach jest wolniejszy lub bardziej skomplikowany, lub masz inny kod, który ma odniesienie do tego samego list i musi być zmutowany, lub cokolwiek innego, dlatego jest to "zwykle", a nie "zawsze".)

Jeśli chcesz zrobić więcej niż raz, to bym owinąć go w funkcji, jak sugeruje Zmienność:

def elements_replaced(lst, new_element, indices): 
    return [new_element if i in indices else e for i, e in enumerate(lst)] 

osobiście prawdopodobnie sprawiają, że generator więc daje iteracji zamiast zwrócenie listy, nawet jeśli nigdy nie będę tego potrzebował, tylko dlatego, że jestem głupia w ten sposób. Ale jeśli rzeczywiście zrobić trzeba go:

myiter = (100 if i in (0, 1, 3) else e for i, e in enumerate(mylist)) 

Lub:

def elements_replaced(lst, new_element, indices): 
    for i, e in enumerate(lst): 
     if i in indices: 
      yield new_element 
     else: 
      yield e 
+0

Dobre wyjaśnienie dotyczące nowej listy i mutacji. – elwc

+0

Czasami tęsknię za 'valarray :: gslice' – Abhijit

+0

@Ahhijit: Cóż, prawie w każdym przypadku, w którym' valarray' ma sens w C++, 'numpy' ma sens w Pythonie, a rozszerzone cięcie' numpy' jest jeszcze prostsze niż 'gslice' dla prawie wszystkich przypadków. Na przykład, patrz odpowiedź MGILSONA na to pytanie. (Oczywiście "prawie każdy" i "prawie wszyscy" to nie to samo, co "każdy" i "wszyscy" ... ale rzadko zdarza się, że piszę kod 'numpy' i brakuje mu funkcji C++ ...) – abarnert

0

Czy tego właśnie szukasz? Zrób listę indeksów, które chcesz zmienić, a następnie przeprowadź pętlę na tej liście, aby zmienić wartości.

els_to_replace = [0, 1, 3] 

mylist = [0, 0, 0, 0, 0] 

for index in els_to_replace: 
    mylist[index] = 100 


mylist 
Out[9]: [100, 100, 0, 100, 0] 
2
def replace_element(lst, new_element, indices): 
    for i in indices: 
     lst[i] = new_element 
    return lst 

To zdecydowanie bardziej ogólne rozwiązanie, a nie jeden-liner chociaż. Na przykład, w przypadku, można by nazwać:

mylist = replace_element(mylist, 100, [0, 1, 3]) 
+0

+1. Oprócz tego, że jest bardziej ogólny, uważam, że jest to najbardziej czytelny sposób na zmianę, mimo że (a raczej prawdopodobnie z powodu) nie jest on jednym linerem. – abarnert

1

Numpy obsługuje tego jeśli nie jesteś przeciwny stosując np.ndarray:

>>> a = np.zeros(5) 
>>> a[[0,1,3]] = 100 
>>> a 
array([ 100., 100., 0., 100., 0.]) 
+0

+1. Nawet jeśli nie odpowiada to wprost na pytanie OP (i nie jestem pewien, czy nie), zawsze, gdy masz do czynienia z sekwencjami liczb i wydaje się, że coś jest trudniejsze niż powinno być, pierwsze pytanie, które powinieneś zadać, prawdopodobnie "Czy jestem przeciwny używaniu tutaj' np.ndarray'? " – abarnert

0

Nie wielkim fanem tego jednego, ale można spróbować (chociaż myślę, że wszystkie powyższe są znacznie bardziej zwięzły i łatwy do odczytania):

In [22]: from operator import setitem 

In [23]: mylist = [0, 0, 0, 0, 0] 

In [24]: indeces_to_replace = [0, 1, 3] 

In [25]: _ = map(lambda x: setitem(mylist, x, 100), indeces_to_replace) 

In [26]: mylist 
Out[26]: [100, 100, 0, 100, 0] 

Oprócz wątpliwej czytelności i konieczność importu, @abarnert wskazał kilka dodatkowych kwestii, a mianowicie, że map nadal tworzy niepotrzebną listę (która jest odrzucana przez _, ale mimo to została utworzona) i że nie będzie działać w Pythonie 3, ponieważ mapreturns an iterator w Pythonie 3.x. Możesz użyć modułu six do symulacji zachowania się map w Pythonie 3.x z Pythona 2.x, oraz w połączeniu z collections.deque (ponownie zgodnie z sugestią @abarnert), można osiągnąć to samo wyjście bez tworzenia dodatkowej listy w pamięci, ponieważ deque, który może zawierać maksymalnie 0 elementów, odrzuci wszystko, co otrzymuje z iteratora map (Zauważ, że z six, map jest symulowany za pomocą itertools.imap).

Ponownie, nie ma absolutnie żadnej potrzeby, aby kiedykolwiek to wykorzystać - każde rozwiązanie powyżej/poniżej jest lepiej :)

In [1]: from collections import deque 

In [2]: from six.moves import map 

In [3]: from operator import setitem 

In [4]: mylist = [0, 0, 0, 0, 0] 

In [5]: indeces_to_replace = [0, 1, 3] 

In [6]: deque(map(lambda x: setitem(mylist, x, 100), indeces_to_replace), maxlen=0) 
Out[6]: deque([], maxlen=0) 

In [7]: mylist 
Out[7]: [100, 100, 0, 100, 0] 
+0

Problem polega na tym, że w Pythonie 3 to nie działa, ponieważ wracasz do iteratora, którego nigdy nie przerabiasz. Najmniejszym problemem jest to, że w Pythonie 2 tworzy listę, której nie potrzebujesz. Możesz rozwiązać pierwszy problem, jawnie robiąc 'list (map (...))'. Lub możesz rozwiązać oba, używając 'six.map' i przekazując wynik do funkcji' iter_discard', którą możesz zaimplementować jako 'collections.deque (it, max_size = 0)'. To wszystko jest o wiele bardziej przemyślane, niż powinno się włożyć w rozwiązanie, na które nie byłeś wielkim fanem, oczywiście ... – abarnert

+0

@abarnert Spóźniony +1 do twojej odpowiedzi (jak zawsze informacyjny) i świetne punkty dotyczące tej implementacji (nigdy nie myślałem o przekazaniu wyników 'map' do funkcji' iter_discard'). Dodam aktualizację dla dobra potomności i dziękuję jak zawsze za pomocne informacje :) – RocketDonkey

+0

Naprawdę chciałbym, aby "iter_discard" było częścią standardowej biblioteki 3.x. Argumentem przeciwko temu było to, że chciałoby to zachęcić ludzi używających 'map' do efektów ubocznych zamiast wyników (a jeśli naprawdę tego chcesz, sztuczka' deque' została dodana do dokumentacji 'itertools' gdzieś). Mogę to kupić ... ale w przypadku tego rodzaju pytań ludzie zawsze tak robią (a potem chcą "timeitować" wyniki z każdą wersją Pythona, którą mogą znaleźć). – abarnert

0

Lubię listowych:

[100 if index in [1, 4] else 0 for index, x in enumerate(mylist) ] 
Powiązane problemy