我听说在Python中不能添加多行lambdas,因为它们会在语法上与Python中的其他语法结构冲突。今天在公交车上,我一直在思考这个问题,并意识到我想不出任何一个Python构造可以与多行lambdas相冲突。考虑到我对这门语言相当熟悉,这让我很惊讶。
现在,我相信Guido没有在语言中包含多行lambda是有原因的,但出于好奇:在什么情况下,包含多行lambda会有歧义?我听说的是真的吗,还是有其他原因导致Python不允许多行lambda ?
我听说在Python中不能添加多行lambdas,因为它们会在语法上与Python中的其他语法结构冲突。今天在公交车上,我一直在思考这个问题,并意识到我想不出任何一个Python构造可以与多行lambdas相冲突。考虑到我对这门语言相当熟悉,这让我很惊讶。
现在,我相信Guido没有在语言中包含多行lambda是有原因的,但出于好奇:在什么情况下,包含多行lambda会有歧义?我听说的是真的吗,还是有其他原因导致Python不允许多行lambda ?
当前回答
在Python 3.8/3.9中有赋值表达式,因此可以在lambda中大量使用 扩展功能
例如,代码
#%%
x = 1
y = 2
q = list(map(lambda t: (
tx := t*x,
ty := t*y,
tx+ty
)[-1], [1, 2, 3]))
print(q)
将打印[3,6,9]
其他回答
这通常是非常丑陋的(但有时替代方案甚至更丑陋),所以一个变通的方法是创建一个大括号表达式:
lambda: (
doFoo('abc'),
doBar(123),
doBaz())
它不会接受任何任务,所以你必须事先准备好数据。 我发现这种方法很有用的地方是PySide包装器,在那里有时会有简短的回调。编写额外的成员函数将更加丑陋。通常你不需要这个。
例子:
pushButtonShowDialog.clicked.connect(
lambda: (
field1.clear(),
spinBox1.setValue(0),
diag.show())
我从python开始,但来自Javascript的最明显的方式是提取表达式作为函数....
人为的例子,乘法表达式(x*2)被提取为函数,因此我可以使用multiline:
def multiply(x):
print('I am other line')
return x*2
r = map(lambda x : multiply(x), [1, 2, 3, 4])
print(list(r))
https://repl.it/@datracka/python-lambda-function
也许它并没有确切地回答这个问题,如果这是如何在lambda表达式本身做多行,但如果有人得到这个线程,看看如何调试表达式(像我),我认为这将有所帮助
让我也说说我对不同变通方法的看法。
一个简单的单行lambda与普通函数有什么不同?我能想到的只有缺少赋值,一些类似循环的结构(for, while), try-except子句……就这样?我们甚至有一个三元运算符——酷!那么,让我们试着解决每一个问题。
作业
这里的一些人正确地指出,我们应该看看lisp的let表单,它允许本地绑定。实际上,所有不改变状态的赋值都只能用let来执行。但是每一个lisp程序员都知道let形式绝对等价于调用lambda函数!这意味着
(let ([x_ x] [y_ y])
(do-sth-with-x-&-y x_ y_))
和
((lambda (x_ y_)
(do-sth-with-x-&-y x_ y_)) x y)
所以绰绰有余!每当我们想要做一个新的赋值时我们只要添加另一个并调用它。想想这个例子:
def f(x):
y = f1(x)
z = f2(x, y)
return y,z
lambda版本如下:
f = lambda x: (lambda y: (y, f2(x,y)))(f1(x))
如果您不喜欢在数据上的操作之后写入数据,您甚至可以创建let函数。你甚至可以咖喱它(只是为了更多的括号:))
let = curry(lambda args, f: f(*args))
f_lmb = lambda x: let((f1(x),), lambda y: (y, f2(x,y)))
# or:
f_lmb = lambda x: let((f1(x),))(lambda y: (y, f2(x,y)))
# even better alternative:
let = lambda *args: lambda f: f(*args)
f_lmb = lambda x: let(f1(x))(lambda y: (y, f2(x,y)))
到目前为止一切顺利。但如果我们必须进行重新分配,也就是改变状态呢?我认为我们完全可以在不改变状态的情况下快乐地生活只要任务不涉及循环。
循环
虽然循环没有直接的lambda替代,但我相信我们可以编写相当通用的函数来满足我们的需求。看看这个斐波那契函数:
def fib(n):
k = 0
fib_k, fib_k_plus_1 = 0, 1
while k < n:
k += 1
fib_k_plus_1, fib_k = fib_k_plus_1 + fib_k, fib_k_plus_1
return fib_k
显然,用是不可能的。但在写了一个小而有用的函数之后,我们就完成了这个和类似的情况:
def loop(first_state, condition, state_changer):
state = first_state
while condition(*state):
state = state_changer(*state)
return state
fib_lmb = lambda n:\
loop(
(0,0,1),
lambda k, fib_k, fib_k_plus_1:\
k < n,
lambda k, fib_k, fib_k_plus_1:\
(k+1, fib_k_plus_1, fib_k_plus_1 + fib_k))[1]
当然,如果可能的话,应该总是考虑使用map、reduce和其他高阶函数。
Try-except和其他控制结构
解决这类问题的一般方法似乎是使用惰性求值,用不接受参数的lambdas替换代码块:
def f(x):
try: return len(x)
except: return 0
# the same as:
def try_except_f(try_clause, except_clause):
try: return try_clause()
except: return except_clause()
f = lambda x: try_except_f(lambda: len(x), lambda: 0)
# f(-1) -> 0
# f([1,2,3]) -> 3
当然,这不是try-except子句的完全替代,但您总是可以使它更通用。顺便说一句,有了这种方法,你甚至可以让if表现得像函数!
总结一下:这是很自然的,所有提到的东西都感觉有点不自然,不那么美丽。尽管如此,它还是有效的!没有任何评价和其他技巧,所以所有的智能感知将工作。我也不是说你应该在任何地方都使用它。通常你最好定义一个普通的函数。我只是证明了没有什么是不可能的。
我很内疚在我的一些更简单的项目中实践了这个肮脏的hack:
lambda args...:( expr1, expr2, expr3, ...,
exprN, returnExpr)[-1]
我希望你能找到一种方法保持python化,但如果你必须这样做,这比使用exec和操纵全局变量要少一些痛苦。
我知道这是一个老问题,但是为了记录,这里有一种多行lambda问题的解决方案,其中一个调用的结果被另一个调用消耗。
我希望它不是超级hack,因为它只是基于标准库函数,没有使用dunder方法。
下面是一个简单的例子,我们从x = 3开始,然后在第一行加1,然后在第二行加2,得到6作为输出。
from functools import reduce
reduce(lambda data, func: func(data), [
lambda x: x + 1,
lambda x: x + 2
], 3)
## Output: 6