12

我很少会在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])))()
4

8 回答 8

18

您可以使用这样的构造来进行柯里化

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

你可以像这样使用它:

>>> add = lambda x, y: x + y
>>> add5 = curry(add, 5)
>>> add5(3)
8
于 2010-12-06T00:17:39.383 回答
4
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])))()

看到最后的 () 了吗?内部 lambda 不返回,它被调用。

该函数相当于

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

但是让我们假设我们想在 lambda 中执行此操作。我们不能在 lambda 中使用赋值。但是,我们可以要求__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])

但是对于 lambda,我们只能有一个表达式。但是由于这些是函数调用,我们可以将它们包装在一个元组中

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]))

Dagnamit,我无法逃脱添加另一个任务!我知道让我们滥用默认参数。

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()

好的,让我们切换到 lambda:

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]

原作者竭尽全力使用 lambda 而不是函数。可能是出于某种原因他不喜欢嵌套函数。我不知道。我要说的是它的错误代码。(除非有一个神秘的理由。)

于 2010-12-06T00:42:08.650 回答
2

它对于临时占位符很有用。假设你有一个装饰器工厂:

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

您可以暂时将其替换为

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

如果它间接返回一个 lambda,它也很有用:

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

它对于在 Python 控制台中创建可调用工厂也很有用。

仅仅因为某事是可能的并不意味着你必须使用它。

于 2010-12-06T00:18:28.260 回答
2

前几天我做了类似的事情来禁用单元测试套件中的测试方法。

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

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

以后很容易重新启用它。

于 2010-12-06T00:39:01.963 回答
1

这可以用来提取一些常见的重复代码(在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
于 2010-12-06T00:22:06.817 回答
0

最常见的是 - 至少在我遇到的代码和我自己编写的代码中 - 用于“冻结”一个具有在创建 lambda 函数时具有的值的变量。否则,nonlocals 变量会引用它们存在的范围内的变量,这有时会导致不希望的结果。

例如,如果我想创建一个包含十个函数的列表,每个函数都是从 0 到 9 的标量的乘数。有人可能会想这样写:

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

无论谁,如果您想使用任何其他分解函数,您都会得到相同的结果:

>>> a[1](10)
90

这是因为在创建 lambda 时未解析 lambda 内的“i”变量。相反,Python 在“for”语句中保留对“i”的引用——在它创建的范围内(这个引用保存在 lambda 函数闭包中)。当 lambda 被执行时,变量被评估,它的值是它在那个范围内的最后一个值。

当一个像这样使用两个嵌套的 lambda 时:

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

“i”变量在“for”循环的执行过程中被评估。它的值被传递给“k” - 并且“k”用作我们要分解的乘法器函数中的非局部变量。对于 i 的每个值,将有一个不同的封闭 lambda 函数实例,以及一个不同的“k”变量值。

所以,有可能达到初衷:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)]
>>> a[1](10)
10
>>> a[9](10)
90
>>>
于 2010-12-06T00:44:49.350 回答
0

它可用于实现更延续/蹦床风格的编程,

请参阅延续传递风格

于 2010-12-06T01:09:18.963 回答
0

基本上,有了这个你可以修改函数而不是值

我最近偶然发现的一个例子:计算近似导数(作为函数)并将其用作另一个地方的输入函数。

dx = 1/10**6    
ddx = lambda f: lambda x: (f(x + dx) - f(x))/dx

f = lambda x: foo(x)
newton_method(func=ddx(f), x0=1, n=10)
于 2021-02-14T17:08:09.433 回答