web-dev-qa-db-ja.com

Pythonに、ジェネレーター関数をリストを返す関数に変換するライブラリ関数はありますか?

ジェネレーターのスタイルは、リストを返すよりも直接的である可能性があると何度も思いました。たとえば、

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

(わかりました。本当に単純な場合は、mapと記述しますが、要点はわかります。ジェネレーターのバージョンの方がクリーンです)。ただし、ジェネレータの戻り値の型は必ずしも望ましいとは限りません。 fooをリストまたはタプルを返す関数に変更するために使用できる組み込みのデコレータはありますか?私が自分で書く方法は、

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)
28
gatoatigrado

私の知る限りでは(そして、まったく同じことを考えていたので、私は調べました)、いいえ:標準ライブラリでこれを行う直接的な方法はありません。

ただし、unstdlib.pyライブラリには徹底的にテストされたlistifyラッパーがあります: 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)
24
David Wolever

@David Woleverの答えは非常にクリーンな方法ですが、(外部デコレータを定義する必要がないため)私がよく行うことの1つは、ジェネレータをローカル関数として次のように記述することです。

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

    return list(gen())
4
Davide

これは、ベルやホイッスルのない、代替のシンプルなデコレータです。

from functools import wraps
from types import GeneratorType

def listify(func):
    """decorator for making generator functions return a list instead"""
    @wraps(func)
    def new_func(*args, **kwargs):
        r = func(*args, **kwargs)
        if isinstance(r, GeneratorType):
            return list(r)
        else:
            return r
    return new_func
1
Granitosaurus