2012-09-11 19 views
20

Un certo numero di volte che ho pensato che lo stile generatore può essere più diretta che restituisce un elenco, ad esempio,Esiste una funzione di libreria in Python per trasformare una funzione generatore in una funzione che restituisce un elenco?

def foo(input_array): 
    for x in input_array: 
     yield processed(x) 

vs.

def bar(input_array): 
    accumulator = [] 
    for x in input_array: 
     accumulator.append(processed(x)) 
    return accumulator 

(va bene, se fosse davvero così semplice , Scriverei map, ma ottieni il punto: la versione del generatore è più pulita). Tuttavia, un tipo di ritorno di un generatore non è sempre desiderato. C'è un decoratore incorporato che posso usare per cambiare foo in una funzione che restituisce una lista o una tupla? Il modo in cui mi piacerebbe scrivere io stesso a dire,

import functools 

def transform_return_value(transformer): 
    def inner(f): 
     @functools.wraps(f) 
     def new_f(*argv, **kwargs): 
      return transformer(f(*argv, **kwargs)) 
     return new_f 
    return inner 

@transform_return_value(list) 
def foo(input_array): 
    for x in input_array: 
     yield processed(x) 
+8

Attendere ... Si desidera scrivere una funzione generatore che restituisca un elenco? Non sono sicuro di capirlo. Perché non scrivere semplicemente 'list (generator_function())', o scrivere 'list_function()' per cominciare? – mgilson

+0

Un'altra scelta è quella di usare la list comprehension: '[elaborato (x) per x in input_array]'. Probabilmente più pulito di entrambi ... –

+1

@mgilson ha ragione: di solito ti converti in lista quando hai bisogno di una lista, che è invariabilmente nel codice cliente. Tendo a documentare le funzioni come restituibili iterabili e mantenere una funzione 'to_sequence (x)' attorno a ciò che effettivamente fa 'list (x) se non isstance (x, Sequence) else x'. –

risposta

19

Per quanto a mia conoscenza (e ho guardato, perché mi sono chiesto esattamente la stessa cosa), no: non c'è modo diretto di fare questo con la libreria standard.

C'è un accuratamente testato listify involucro in biblioteca unstdlib.py, però: https://github.com/shazow/unstdlib.py/blob/master/unstdlib/standard/list_.py#L149

def listify(fn=None, wrapper=list): 
    """ 
    A decorator which wraps a function's return value in ``list(...)``. 

    Useful when an algorithm can be expressed more cleanly as a generator but 
    the function should return an list. 

    Example:: 

     >>> @listify 
     ... def get_lengths(iterable): 
     ...  for i in iterable: 
     ...   yield len(i) 
     >>> get_lengths(["spam", "eggs"]) 
     [4, 4] 
     >>> 
     >>> @listify(wrapper=tuple) 
     ... def get_lengths_tuple(iterable): 
     ...  for i in iterable: 
     ...   yield len(i) 
     >>> get_lengths_tuple(["foo", "bar"]) 
     (3, 3) 
    """ 
    def listify_return(fn): 
     @wraps(fn) 
     def listify_helper(*args, **kw): 
      return wrapper(fn(*args, **kw)) 
     return listify_helper 
    if fn is None: 
     return listify_return 
    return listify_return(fn) 
+0

"la mia libreria di strumenti che copio + incolla tra i progetti" Non so se sentirmi sollevato o sgomento di non essere l'unico a farlo. Ho una manciata di funzioni Python che sembrano far parte della maggior parte dei miei progetti Python. Copiare e incollare sembra grossolano, ma sono così piccoli (e non correlati l'uno all'altro) che metterli in una biblioteca sembra eccessivo. –

+0

Ah ah! Da quando ho scritto questa risposta ho iniziato esattamente una tale libreria: https://github.com/shazow/unstdlib.py –

+0

Preferisco chiamare il decoratore qualcosa come 'wrap_return', quindi fare' listify = wrap_return (wrapper = list) 'invece di usare' @listify (wrapper = Counter) ' –

-2

Per le definizioni efficienti e concise lista provare a utilizzare la lista di comprensione:

def foo(input_array): 
    return [processed(x) for x in input_array] 

Se si desidera un funzione per restituire una lista, fargli restituire una lista. Questo è molto più pulito, più facile da capire, leggere ed eseguire il debug rispetto all'uso del decoratore.

Si consiglia di scrivere questo inline, piuttosto che chiamare una funzione.

+5

Questo non risponde alla domanda dell'OP. L'OP sta descrivendo situazioni in cui è più pulito implementare una funzione utilizzando un generatore, ma il valore di ritorno di tale funzione deve essere una lista. In quelle situazioni, è del tutto ragionevole implementare una funzione come generatore, quindi avvolgerla in un decoratore che scarta quel generatore in un elenco. –

+0

@David Ed è ancora più pulito usare la comprensione delle liste! –

+3

Sì, questo * esempio specifico * è più pulito come una comprensione di lista. Ma la domanda non è "come pulisco l'implementazione di questa funzione?", La domanda è "Esiste una funzione di libreria in Python per trasformare una funzione generatore in una funzione che restituisce una lista?" –

3

Anche se la risposta di @ David Wolever è suerly il modo più pulito, una cosa che mi ritrovo spesso a fare (in quanto non richiede di definire un decoratore esterno) sta scrivendo il generatore in funzione del luogo, in questo modo:

def foo(input_array): 
    def gen(): 
     for x in input_array: 
      yield processed(x) 

    return list(gen()) 
Problemi correlati