2013-01-16 7 views
7

Ho una lista: mylist = [0, 0, 0, 0, 0]Sostituzione elementi di una lista selezionata in Python

voglio solo sostituire gli elementi selezionati, dicono che il primo, il secondo e il quarto con un numero comune, A = 100.

Un modo per fare questo:

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

Sto cercando una battuta, o un metodo più semplice per fare questo. È preferibile una risposta più generale e flessibile.

+0

ne dici di questo? mylist [: 2], mylist [3] = [A] * 2, A –

risposta

7

Tanto più che si sta sostituendo una fetta considerevole della list, farei questo immutabilmente:

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

E 'intenzionale in Python che per fare un nuovo list è un one-liner, mentre mutando un list richiede un ciclo esplicito. Di solito, se non sai quale vuoi, vuoi il nuovo list. (In alcuni casi è più lento o più complicato, oppure hai qualche altro codice che ha un riferimento allo stesso list e ha bisogno di vederlo mutato, o qualsiasi altra cosa, che è il motivo per cui è "di solito" piuttosto che "sempre".)

Se si vuole fare questo più di una volta, mi piacerebbe avvolgetelo in una funzione, come suggerisce Volatilità:

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

io personalmente sarebbe probabilmente renderlo un generatore in modo che produce un'iterazione invece di restituire una lista, anche se non avrò mai bisogno di questo, solo perché sono stupido in quel modo. Ma se effettivamente fai bisogno:

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

Oppure:

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

Buona spiegazione per quanto riguarda la nuova lista e la mutazione. – elwc

+0

A volte mi manca il 'valarray :: gslice' – Abhijit

+0

@Abhijit: Beh, quasi tutti i casi in cui' valarray' ha senso in C++, 'numpy' ha senso in Python -e l'estensione di' numpy' è ancora più semplice di 'gslice' per quasi tutti i casi. Ad esempio, vedere la risposta di mgilson a questa domanda. (Ovviamente "quasi tutti" e "quasi tutti" non sono esattamente come "tutti" e "tutti" ... ma è raro che mi ritrovi a scrivere un codice 'numpy' e manchi una funzione C++ ...) – abarnert

0

È questo quello che stai cercando? Creare un elenco degli indici che si desidera modificare e quindi scorrere l'elenco per modificare i valori.

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 

E 'sicuramente una soluzione più generale, non una battuta però. Ad esempio, nel tuo caso, si chiamerebbe:

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

+1. Oltre a essere più generale, penso che questo sia il modo più leggibile di fare la sostituzione mutabile, nonostante (o, piuttosto, probabilmente a causa di) non essere un one-liner. – abarnert

1

Numpy supporta questo se non sei invece di utilizzare un np.ndarray:

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

+1. Anche se questo non risponde direttamente alla domanda dell'OP (e non sono sicuro che non lo sia), ogni volta che ti trovi a dover gestire sequenze di numeri e qualcosa sembra più difficile di come dovrebbe essere, la prima domanda che dovresti fare è probabilmente "Sono contrario ad usare un' np.ndarray' qui? " – abarnert

0

Non è un grande fan di questo, ma si potrebbe provare questo (anche se credo che tutto quanto sopra sono molto più conciso e di facile lettura):

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] 

a parte la leggibilità discutibile e la necessità di un'importazione, @abarnert ha sottolineato un paio di ulteriori problemi, vale a dire che 012.369.crea ancora un elenco non necessario (che viene scartato con lo _ ma creato comunque) e che non funzionerà in Python 3 perché mapreturns an iterator in Python 3.x. È possibile utilizzare il modulo six per simulare il comportamento di map in Python 3.x da Python 2.x, e in combinazione con collections.deque (sempre come suggerito da @abarnert), è possibile ottenere lo stesso output senza creare l'elenco aggiuntivo in memoria perché uno deque che può contenere un massimo di 0 elementi scarterà tutto ciò che riceve dall'iter map (si noti che con six, viene simulato map utilizzando itertools.imap).

Anche in questo caso, non c'è assolutamente alcuna necessità di utilizzare sempre questo - ogni soluzione sopra/sotto è meglio :)

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

il problema con questo è che in Python 3, non funziona, perché si recupera un iteratore che non si itera mai. Il problema minore è che in Python 2 crea un elenco di cui non hai bisogno. Puoi risolvere il primo problema facendo esplicitamente 'list (map (...))'. Oppure puoi risolvere entrambi usando 'six.map' e passando il risultato ad una funzione' iter_discard', che puoi implementare come 'collections.deque (it, max_size = 0)'. Il che è tutto molto più pensato di quanto dovresti mai mettere in una soluzione di cui non eri un grande fan, ovviamente ... – abarnert

+0

@abarnert Tardato +1 alla tua risposta (informativo come sempre) e ottimi punti riguardo a questa implementazione (mai pensato di passare i risultati di 'map' a una funzione' iter_discard'). Aggiungerò un aggiornamento per la posterità, e grazie come sempre per le informazioni utili :) – RocketDonkey

+0

Vorrei davvero che 'iter_discard' facesse parte della libreria standard 3.x. L'argomentazione contro di essa era che questo avrebbe incoraggiato le persone ad usare 'map' per gli effetti collaterali invece dei risultati (e, se lo si desidera veramente, il trucco' deque' è stato aggiunto alla documentazione per 'itertools' da qualche parte). Posso comprarlo ... ma per questo tipo di domande, la gente lo fa sempre comunque (e poi vuole "timeit" i risultati con ogni versione di Python che possono trovare). – abarnert

0

mi piace una lista di comprensione:

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