лямбда возвращает лямбду в питоне - PullRequest
10 голосов
/ 06 декабря 2010

Очень редко я сталкиваюсь с кодом на python, который использует анонимную функцию, которая возвращает анонимную функцию ...?

К сожалению, я не могу найти пример под рукой, но обычно он принимает форму:

g = lambda x,c: x**c lambda c: c+1

Зачем кому-то это делать? Может быть, вы можете привести пример, который имеет смысл (я не уверен, что тот, который я сделал, имеет какой-то смысл).

Редактировать: Вот пример:

swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])),
       f(y,a[x][0]),f(x,a[x][1])))()

Ответы [ 7 ]

16 голосов
/ 06 декабря 2010

Вы можете использовать такую ​​конструкцию, чтобы сделать карри :

curry = lambda f, a: lambda x: f(a, x)

Вы можете использовать ее как:

>>> add = lambda x, y: x + y
>>> add5 = curry(add, 5)
>>> add5(3)
8
2 голосов
/ 06 декабря 2010
swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])),
       f(y,a[x][0]),f(x,a[x][1])))()

Видите () в конце?Внутренняя лямбда не возвращается, ее называют.

Функция делает эквивалент

def swap(a, x, y):
     a[x] = (a[x], a[y])
     a[y] = a[x][0]
     a[x] = a[x][1]

Но давайте предположим, что мы хотим сделать это в лямбда-выражении.Мы не можем использовать назначения в лямбде.Однако мы можем вызвать __setitem__ для того же эффекта.

def swap(a, x, y):
     a.__setitem__(x, (a[x], a[y]))
     a.__setitem__(y, a[x][0])
     a.__setitem__(x, a[x][1])

Но для лямбды мы можем иметь только одно выражение.Но так как это вызовы функций, мы можем заключить их в кортеж

def swap(a, x, y):
     (a.__setitem__(x, (a[x], a[y])),
     a.__setitem__(y, a[x][0]),
     a.__setitem__(x, a[x][1]))

Однако все эти __setitem__ вызывают у меня недовольство, поэтому давайте разберемся с ними:

def swap(a, x, y):
     f = a.__setitem__
     (f(x, (a[x], a[y])),
     f(y, a[x][0]),
     f(x, a[x][1]))

Дагнамит, я не могу сойти с рук, добавив еще одно задание!Я знаю, давайте злоупотребим параметрами по умолчанию.

def swap(a, x, y):
     def inner(f = a.__setitem__):
         (f(x, (a[x], a[y])),
         f(y, a[x][0]),
         f(x, a[x][1]))
     inner()

Хорошо, давайте переключимся на лямбды:

swap = lambda a, x, y: lambda f = a.__setitem__: (f(x, (a[x], a[y])), f(y, a[x][0]),  f(x, a[x][1]))()

Что возвращает нас к исходному выражению (плюс / минус опечаток)

Все это возвращает нас к вопросу: Почему?

Функция должна быть реализована как

def swap(a, x, y):
    a[x],a[y] = a[y],a[x]

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

1 голос
/ 06 декабря 2010

Я сделал что-то подобное на днях, чтобы отключить метод тестирования в комплекте unittest.

disable = lambda fn : lambda *args, **kwargs: None

@disable
test_method(self):
    ... test code that I wanted to disable ...

Легко включить его позже.

1 голос
/ 06 декабря 2010

Это можно использовать для извлечения некоторого распространенного повторяющегося кода (конечно, есть и другие способы добиться этого в python).

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

import sys
prefixer = lambda prefix: lambda message: sys.stderr.write(prefix + ":" + message + "\n")
log_error = prefixer("ERROR")
log_warning = prefixer("WARNING")
log_info = prefixer("INFO")
log_debug = prefixer("DEBUG")

log_info("An informative message")
log_error("Oh no, a fatal problem")

Эта программа печатает

   INFO:An informative message
   ERROR:Oh no, a fatal problem
1 голос
/ 06 декабря 2010

Может быть полезно для временных заполнителей.Предположим, у вас есть фабрика декораторов:

@call_logger(log_arguments=True, log_return=False)
def f(a, b):
    pass

Вы можете временно заменить ее на

call_logger = lambda *a, **kw: lambda f: f

Это также может быть полезно, если она косвенно возвращает лямбду:

import collections
collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(int)))

Это также полезно для создания вызываемых фабрик в консоли Python.

И то, что что-то возможно, не означает, что вы должны его использовать.

0 голосов
/ 06 декабря 2010

Может использоваться для достижения более продолжительного / трамплинного стиля программирования,

См. Стиль продолжения

0 голосов
/ 06 декабря 2010

Чаще всего - по крайней мере, в коде, который я встречал и который я сам пишу, - "замораживали" переменную со значением, которое она имеет в момент создания лямбда-функции. В противном случае нелокальная переменная ссылается на переменную в той области, в которой они существуют, что иногда может привести к нежелательным результатам.

Например, если я хочу создать список из десяти функций, каждая из которых является множителем для скаляра от 0 до 9. Можно было бы написать это так:

>>> a = [(lambda j: i * j) for i in range(10)]
>>> a[9](10)
90

Кто угодно, если вы хотите использовать любую из других факторизованных функций, вы получите тот же результат:

>>> a[1](10)
90

Это потому, что переменная "i" внутри лямбды не разрешается при создании лямбды. Скорее, Python сохраняет ссылку на «i» в операторе «for» - в той области действия, в которой он был создан (эта ссылка сохраняется в закрытии лямбда-функции). Когда лямбда выполняется, переменная вычисляется, и ее значение является последним значением, которое она имела в этой области.

Когда используется две вложенные лямбды, как это:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)]

Переменная «i» вычисляется во время выполнения цикла «for». Значение ś передается в «k», а «k» используется в качестве нелокальной переменной в функции множителя, которую мы вычленяем. Для каждого значения i будет свой экземпляр лямбда-функции и другое значение для переменной «k».

Итак, можно достичь первоначального намерения:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)]
>>> a[1](10)
10
>>> a[9](10)
90
>>>
...