2012-09-11 33 views
20

Un número de veces que he pensado que el estilo generador puede ser más directa que la devolución de una lista, por ejemplo,¿Existe una función de biblioteca en Python para convertir una función de generador en una función que devuelve una lista?

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 

(bien, si fuera así de simple , Escribiría map, pero entiendes: la versión del generador es más limpia). Sin embargo, un tipo de retorno de un generador no siempre es deseado. ¿Hay un decorador incorporado que pueda usar para cambiar foo en una función que devuelva una lista o tupla? La forma en que me gustaría escribir yo mismo es,

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

Espera ... ¿Quieres escribir una función de generador que devuelva una lista? No estoy seguro de entender esto. ¿Por qué no simplemente escribir 'list (generator_function())', o escribir 'list_function()' para comenzar? – mgilson

+0

Otra opción es usar la comprensión de listas: '[procesado (x) para x en input_array]'. Posiblemente más limpio que ambos ... –

+1

@mgilson tiene razón: por lo general, convertirías a la lista cuando necesites una lista, que invariablemente está en el código del cliente. Tiendo a documentar funciones como iterables que regresan y mantener una función 'to_sequence (x)' alrededor que efectivamente '' lista (x) si no esinstancia (x, Secuencia) else x'. –

Respuesta

19

A lo mejor de mi conocimiento (y he buscado, porque me he preguntado exactamente lo mismo), no: no hay forma directa de hacerlo esto con la biblioteca estándar.

Hay un probado a fondo listify envoltorio en la biblioteca unstdlib.py, sin embargo: 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

"mi biblioteca de herramientas que copio y pego entre proyectos" No sé si sentirme aliviado o consternado porque no soy el único que hace esto. Tengo un puñado de funciones de Python que parecen estar en la mayoría de mis proyectos de Python. Copiar y pegar parece asqueroso, pero son tan pequeños (y no están relacionados entre sí) que ponerlos en una biblioteca parece exagerado. –

+0

¡Ah, ja! Desde que escribí esta respuesta comencé exactamente una biblioteca así: https://github.com/shazow/unstdlib.py –

+0

Prefiero llamar al decorador algo así como 'wrap_return' luego hacer' listify = wrap_return (wrapper = list) 'en lugar de usar' @listify (wrapper = Counter) ' –

-2

Para las definiciones de lista eficientes y concisas intente usar lista por comprensión:

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

Si desea una función para devolver una lista, haga que devuelva una lista. Esto es mucho más limpio, más fácil de entender, leer y depurar que usar el decorador.

Puede preferir escribir esto en línea, en lugar de llamar a una función.

+5

Esto no responde a la pregunta del OP. El OP está describiendo situaciones en las que es más limpio implementar una función usando un generador, pero el valor de retorno de esa función debe ser una lista. En esas situaciones, es totalmente razonable implementar una función como generador, luego envolverlo en un decorador que desenvuelva ese generador en una lista. –

+0

@David ¡Y aún es más limpio usar la lista de comprensión! –

+3

Sí, este * ejemplo específico * es más limpio como una lista de comprensión. Pero la pregunta no es "¿cómo puedo limpiar la implementación de esta función?", La pregunta es "¿Existe una función de biblioteca en Python para convertir una función de generador en una función que devuelve una lista?" –

3

Aunque la respuesta de @ David Wolever es suerly la manera más limpia, una cosa que a menudo me encuentro haciendo (ya que no requiere definir un decorador externa) está escribiendo el generador como una función local, así:

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

    return list(gen()) 
Cuestiones relacionadas