È possibile aggiungere una clausola where con elenco di comprensione?

Si consideri il seguente elenco di comprensione

[ (x,f(x)) for x in iterable if f(x) ]

Questo filtra i iterable base di una condizione f e restituisce le coppie di x,f(x). Il problema con questo approccio è f(x) viene calcolato due volte.
Sarebbe bello se si potesse scrivere come

[ (x,fx) for x in iterable if fx where fx = f(x) ]
or
[ (x,fx) for x in iterable if fx with f(x) as fx ]

Ma in python che dobbiamo scrivere utilizzando nidificati genericità per evitare di duplicare la chiamata di f(x) e rende la comprensione, l’aspetto meno chiaro

[ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ]

C’è qualche altro modo per rendere più divinatori e leggibile?


Aggiornamento

Presto in python 3.8! PEP

# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
  • Sei sicuro che è calcolato due volte quando si hanno compilato?
  • Non sono sicuro di come compilare. Ma sul prompt di python, è eseguita due volte. Ho controllato con l’aggiunta di un istruzione di stampa.
  • Se non si desidera calcolare f(x) due volte, tenta di aggiungere una cache in f().
  • sì, python chiamata f(x) due volte per ogni x in iterable nella prima istruzione.
  • Sì. è anche uno dei modi possibili. Ma sto cercando di trovare un modo più pulito. Aggiunta di una cache avrà un dizionario di look-up e potrebbe essere un overkill per semplici espressioni lambda.
  • questo non è tanto un “[... where ...] clausola”, tanto che vogliono ottimizzare il [... if ...] clausola e/o per l’introduzione di letstile anonimo associazioni.
  • sì, let è esattamente il termine.
  • Si prega di spostare il tuo “aggiornamento” la sezione dall’interno la domanda di una nuova risposta. Vedere si Dovrebbe mettere una risposta alla tua domanda e segnare come accettato di rispondere o aggiornare la tua domanda? e che Cosa è l’azione appropriata quando la risposta alla domanda è aggiunto per la stessa questione? per una spiegazione.

InformationsquelleAutor balki | 2012-07-23

 

4 Replies
  1. 10

    Si cerca di avere let-istruzione semantica in python list comprehensions, il cui ambito di applicazione è disponibile sia per il ___ for..in(mappa) e il if ___(filtro) parte della comprensione, e la cui portata dipende dalla ..for ___ in....


    Soluzione, modificato:
    Il tuo (come ammettere illeggibile) soluzione di [ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ] è il modo più semplice per scrivere l’ottimizzazione.

    Idea principale: ascensore x, nella tupla (x,f(x)).

    Alcuni sostengono che la maggior parte dei “divinatori” modo di fare le cose sarebbero originale [(x,f(x)) for x in iterable if f(x)] e di accettare le inefficienze.

    È, tuttavia, possibile fattore di fuori del ((y,fy) for y in iterable) in una funzione, se avete intenzione di fare un sacco di tutto questo. Questo è un male perché se mai il desiderio di avere accesso a più variabili x,fx (es. x,fx,ffx), allora si avrà bisogno di riscrivere tutti i tuoi list comprehensions. Quindi questa non è una grande soluzione, se si sa per certo che solo bisogno di x,fx e il piano per il riutilizzo di questo modello.


    Generatore di espressione:

    Idea: utilizzare un più complicato, in alternativa al generatore di espressioni: quella in cui python permette di scrivere più righe.

    Si potrebbe utilizzare un generatore di espressione, che python gioca bene con:

    def xfx(iterable):
        for x in iterable:
            fx = f(x)
            if fx:
                yield (x,fx)
    
    xfx(exampleIterable)

    Questo è come vorrei personalmente fare.


    Memoization/cache:

    Idea: Si potrebbe anche uso(abuso?) effetti collaterali e fare f globale memoization cache, in modo da non ripetere le operazioni.

    Questo può avere un po ‘ di overhead, e richiede una politica di quanto è grande la cache dovrebbe essere e quando dovrebbe essere di garbage collection. Quindi questo deve essere utilizzato solo se ti serve anche per altre memoizing f, o, se f è molto costoso. Ma essa consente di scrivere…

    [ (x,f(x)) for x in iterable if f(x) ]

    …come in origine voleva senza il calo di prestazioni di fare le costose operazioni di f due volte, anche se tecnicamente è chiamata due volte. È possibile aggiungere un @memoized decoratore per f: esempio (senza dimensione massima della cache). Questo funziona finché x è hashable (ad esempio, un numero, una tupla, un frozenset, etc.).


    Valori fittizi:

    Idea principale: acquisizione fx=f(x) in una chiusura e modificare il comportamento dell’elenco di comprensione.

    filterTrue(
        (lambda fx=f(x): (x,fx) if fx else None)() for x in iterable
    )

    dove filterTrue(iterable) è il filtro(Nessuno, iterable). È necessario modificare il tipo di lista (2 tupla) era in realtà in grado di essere None.

    • Plus xfx() potrebbe essere generalizzata, passando l’ f funzione come argomento.
  2. 10

    Non c’è where economico, ma è in grado di “emulare” usando for:

    a=[0]
    def f(x):
        a[0] += 1
        return 2*x
    
    print [ (x, y) for x in range(5) for y in [f(x)] if y != 2 ]
    print "The function was executed %s times" % a[0]

    Di esecuzione:

    $ python 2.py 
    [(0, 0), (2, 4), (3, 6), (4, 8)]
    The function was executed 5 times

    Come si può vedere, le funzioni è eseguito 5 volte, non 10 o 9.

    Questo for costruzione:

    for y in [f(x)]

    imitare clausola where.

  3. 5

    Nulla dice che si deve usare genericità. In effetti la maggior parte delle guide di stile ho visto la richiesta di limitare a semplici costrutti, comunque.

    Si potrebbe utilizzare un generatore di espressione, invece.

    def fun(iterable):
        for x in iterable:
            y = f(x)
            if y:
                yield x, y
    
    
    print list(fun(iterable))
  4. 3

    Mappa e Zip ?

    fnRes = map(f, iterable)
    [(x,fx) for x,fx in zip(iterable, fnRes) if fx)]
    • y==1 è estremamente scarsa forma; y è già un valore booleano, si può solo dire if y (piuttosto che confrontando la Vera==1 / False==1; e ‘un po’ come dire:return myBoolean==bool(1), che è peggio di return myBoolean==True, piuttosto che il solito return myBoolean). y potrebbe anche essere chiamato qualcosa semanticamente significativo, come fx. Oltre a questo, questa è una risposta ragionevole. [edit: +1 =)]
    • grazie per il suggerimento. Modificato di conseguenza
    • Non funzionerà se il iterable è un generatore. Sarà necessario utilizzare itertools.tee per ottenere due iteratori.
    • Buon punto, anche questo potrebbe essere inefficiente in alcuni casi.

Lascia un commento