2014-10-31 14 views
10

Ho un metodo che prende una lista e restituire un oggettoPython: come impostare la variabile locale nella comprensione degli elenchi?

# input a list, returns an object 
def map_to_obj(lst): 
    a_list = f(lst) 
    return a_list[0] if a_list else None 

voglio ottenere un elenco che contiene tutti gli elementi mappati che non è None.

Ti piace questa:

v_list = [v1, v2, v3, v4] 

[map_to_obj(v) for v in v_list if map_to_obj(v)] 

Ma sembra non è bene chiamare il metodo map_to_obj due volte nella lista di comprensione.

C'è un modo per avere variabili locali nella comprensione delle liste in modo che possa avere prestazioni migliori?

Oppure il compilatore lo ottimizza automaticamente?

Ecco quello che voglio:

(sml like) 
[let mapped = map_to_obj(v) in for v in v_list if mapped end] 

risposta

33

Usa lista annidata di comprensione:

[x for x in [map_to_obj(v) for v in v_list] if x]

o meglio ancora, una lista di comprensione intorno a un generatore di espressione:

[x for x in (map_to_obj(v) for v in v_list) if x]

+3

È una bella risposta. Le risposte non dovrebbero avere punti interrogativi. – Paul

+0

È una risposta soddisfacente e, naturalmente, è la stessa di [behzad] (http://stackoverflow.com/users/625914/behzad-nouri), con una comprensione delle liste al posto di 'map' e' filter' ... Ho intenzione di andare su di nuovo come mi piaceva come [Lying Dog] (http: // StackOverflow.it/users/4134826/lying-dog) ha tradotto 'filter' in termini di l-c, ma l'OP può approvare una di queste risposte in quanto entrambe sono risposte utili e utili. – gboffi

+2

La comprensione interiore dovrebbe essere un'espressione generatrice. Non c'è bisogno di costruire l'intera lista e solo poi buttare via gli oggetti vuoti per costruire ancora un altro elenco. –

3

È possibile evitare di ri-calcolo utilizzando python built-in filter:

list(filter(lambda t: t is not None, map(map_to_obj, v_list))) 
+0

Ma anche questo itera l'elenco due volte ... –

+0

Esiste una soluzione di iterazione? –

+0

@HaoTan non in python 3; in python 3, 'map' restituisce un oggetto mappa non un elenco e' filter' restituisce un oggetto filtro; quindi, questo _chain_ le funzioni senza creare liste intermedie. –

0

di lista vanno bene per i casi più semplici, ma a volte un semplice vecchio for Loop è la soluzione più semplice:

other_list = [] 
for v in v_list: 
    obj = map_to_obj(v) 
    if obj: 
     other_list.append(obj) 

Ora, se si vuole veramente un elenco comp e dont vuole costruire un elenco tmp, è possibile utilizzare le versioni iteratore di filter e map:

import itertools as it 
result = list(it.ifilter(None, it.imap(map_to_obj, v_list))) 

o più semplicemente:

import itertools as it 
result = filter(None, it.imap(map_to_obj, v_list))) 

Le versioni iteratore non costruire una lista temporanea, usano valutazione pigra.

0

ho trovato un modo di usare reduce:

def map_and_append(lst, v): 
    mapped = map_to_obj(v) 
    if mapped is not None: 
     lst.append(mapped) 
    return lst 

reduce(map_and_append, v_list, []) 

Come circa le prestazioni di questo?

+0

Puoi usare il modulo' timeit' per cronometrare le diverse soluzioni, ma il tuo snippet precedente è un modo arbitrariamente sovracompensato per fare una cosa molto semplice - e dubito che sarà più veloce o più efficiente rispetto al semplice vecchio ciclo per le soluzioni filtro/imap ... –

+0

@Bruno Mi piace il tuo «troppo complesso»! – gboffi

+0

Se guardo le risposte di itertools no, nonostante la performance, oserei dire [quella di Lying Dog] (http://stackoverflow.com/a/26672589/2749397) è la più espressiva dell'intenzione, e quindi la più leggibile. E per quanto riguarda le prestazioni? beh, non so quanta CPU passi nelle tue chiamate 'f (lst)' ma rimuovendo il 'None 'in un modo o nell'altro è improbabile che cambi l'intera immagine. – gboffi

Problemi correlati