繁体   English   中英

lambda 在 python 中返回 lambda

[英]lambda returns lambda in python

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

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

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

你可能会像这样使用它:

>>> add = lambda x, y: x + y
>>> add5 = curry(add, 5)
>>> add5(3)
8
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]))

该死,我不能再添加另一个任务了! 我知道让我们滥用默认参数。

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

好的,让我们切换到 lambdas:

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 而不是函数。 可能是因为某种原因他不喜欢嵌套函数。 我不知道。 我要说的是它的糟糕代码。 (除非有一个神秘的理由。)

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

@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 控制台中创建可调用工厂也很有用。

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

这可以用来拉出一些常见的重复代码(在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

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

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

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

以后很容易重新启用它。

它最常 - 至少在我遇到并且我自己编写的代码中 - 用于“冻结”一个变量,它在创建 lambda 函数时具有它的值。 否则,非局部变量引用它们存在的范围内的变量,这有时会导致不受欢迎的结果。

例如,如果我想创建一个包含十个函数的列表,每个函数都是一个从 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”循环执行期间被评估。 Itś 值传递给“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
>>>

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

参见Continuation-passing style

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

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

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM