Pythonic идиома для построения списка из анонимного генератора - PullRequest
0 голосов
/ 05 мая 2018

Я считаю генераторы на основе yield очень полезными для операций с последовательностями, которые слишком сложны, чтобы использовать списки. Предположим, я хочу построить список из специализированного генератора. Я могу написать это как:

def gen():
    for <whatever>:
        yield x

xs = list(gen())

Большую часть времени мне нужно оценивать этот генератор только один раз, поэтому ему не нужно имя. Если бы у Python были многострочные labmd, я мог бы использовать:

xs = list((lambda:
    for <whatever>:
        yield x)())

но в Python нет многострочных лямбд. Есть ли другой способ получить что-то подобное?

(примечание: пожалуйста, не предлагайте использовать списочные выражения; я говорю о случаях, когда логика, когда yield является более сложной.)

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

Декораторы не имеют для возврата функции.

def genlist(g):
    return list(g())


@genlist
def xs():  # for <whatever>
    for x in range(3):
        yield x*x
    for x in range(3):
        yield x+x
    yield 42


print(xs)

[0, 1, 4, 0, 2, 4, 42]

В большинстве случаев, когда rubyist хочет блок в Python, это может быть выполнено с помощью декоратора, подобного этому.

0 голосов
/ 07 мая 2018

Если логика слишком сложна для выражения генератора, вам, вероятно, не следует использовать лямбду. Это не то же самое, что сказать, что ты не можешь.

В Python-лямбдах может быть столько символов новой строки, сколько вы хотите. Они просто не могут содержать операторов , только выражения. Вы можете использовать yield выражение (не yield выражение - вам нужны дополнительные скобки) в лямбде.

>>> list((lambda: (yield 1))())
[1]

Не так ли полезно, не так ли? Вы можете выполнить список выражений доходности, используя ... выражение отображения списка - []. (Python гарантирует, что элементы дисплеев будут оцениваться по порядку.)

>>> tuple((lambda:[
    (yield 1),
    (yield 2),
    (yield 3),
])())
(1, 2, 3)

Кортежи тоже работают. Посмотри на это. Несколько «строк» ​​(не операторов). Все еще не так полезно. Вы хотите иметь возможность зацикливаться. Но утверждение for - это утверждение. (Опять же, почему бы не использовать выражение-генератор? Эти являются выражениями.) На практике это не такое ограничение, поскольку лямбда-выражения могут вызывать функции, содержащие операторы.

>>> def loop(itr, body):
    for x in itr:
        yield body(x)


>>> list(loop(range(9), lambda x: x*x))
[0, 1, 4, 9, 16, 25, 36, 49, 64]

Вот как сделать цикл for выражением без использования понимания. Но мы хотим петлю внутри лямбды.

>>> list((lambda:
      (yield from loop(range(9), lambda x:
               x*x))
      )())
[0, 1, 4, 9, 16, 25, 36, 49, 64]

Да, yield from также является выражением. Если вы заключите его в скобки. Я думаю, что хорошо выделенные def легче читать, хотя. Вы можете дать ему короткое одноразовое имя, например _f или что-то еще.


Лямбда-исчисление является самостоятельным по Тьюрингу, способным вычислять все, что можно вычислить.

Это означает, что технически нам даже не нужна функция loop. Вместо этого вы можете использовать рекурсию. (Несмотря на то, что в Python вы в конечном итоге получите переполнение стека.) Но как вы можете использовать анонимную функцию?

Вы используете «декоратор», чтобы дать ему имя.

>>> def recur(func):
    def wrapper(*args, **kwargs):
        return func(func, *args, **kwargs)
    return wrapper

>>> tuple(recur(lambda r, n:[
    (yield n),
    (yield from r(r, n-1)) if n else 0]
     )(3))
(3, 2, 1, 0)

Конечно, «декоратор» тоже может быть анонимным.

>>> tuple((lambda f: lambda *a, **kw: f(f, *a, **kw))(lambda r, n:[
    (yield n),
    (yield from r(r, n-1)) if n else 0]
     )(3))
(3, 2, 1, 0)

Во-первых, я не писал это так, потому что ваши глаза застеклились бы, увидев слишком много лямбд. Вот почему вы должны использовать def!

Если вас интересует, как далеко мы можем толкать лямбды, как это, посмотрите Drython , в котором есть аналогичные замены выражений для каждого оператора Python.

...