我听说它说多行lambda不能在Python中添加,因为它们会在语法上与Python中的其他语法结构发生冲突。我今天在公共汽车上考虑这个问题,并意识到我无法想到多线lambdas冲突的单个Python构造。鉴于我非常了解这门语言,这让我感到惊讶。
现在,我确信Guido有理由不在语言中包含多行lambdas,但出于好奇:包含多行lambda的情况会有多么模糊?我听到的是真的,还是有其他原因让Python不允许多行lambda?
答案 0 :(得分:544)
Guido van Rossum(Python的发明者)在an old blog post中自己回答了这个问题 基本上,他承认这在理论上是可行的,但任何提议的解决方案都是非Pythonic:
“但是对于这个难题的任何提议解决方案的复杂性对我来说是巨大的:它需要解析器(或更确切地说,词法分析器)能够在缩进敏感和缩进不敏感模式之间来回切换,保持一堆先前的模式和缩进级别。从技术上讲,这一切都可以解决(已经有一堆可以推广的缩进级别。)但这些都没有消除我的直觉,认为它是一个精心设计的Rube Goldberg contraption 。“
答案 1 :(得分:131)
请看以下内容:
map(multilambda x:
y=x+1
return y
, [1,2,3])
这是一个返回(y, [1,2,3])
的lambda(因此map只获取一个参数,导致错误)?还是会返回y
?或者它是语法错误,因为新行上的逗号是错误的? Python如何知道你想要什么?
在parens中,缩进与python无关,因此您无法明确地使用多行。
这只是一个简单的例子,可能还有更多例子。
答案 2 :(得分:44)
这通常非常难看(但有时替代方案甚至更难看),因此解决方法是制作大括号表达式:
lambda: (
doFoo('abc'),
doBar(123),
doBaz())
但它不接受任何作业,因此您必须事先准备好数据。 我发现这个有用的地方是PySide包装器,你有时会有短回调。编写额外的成员函数会更加难看。通常你不需要这个。
示例:
pushButtonShowDialog.clicked.connect(
lambda: (
field1.clear(),
spinBox1.setValue(0),
diag.show())
答案 3 :(得分:17)
一些相关链接:
有一段时间,我正在关注Reia的开发,Reia最初也将使用基于Python的基于缩进的语法和Ruby块,所有这些都在Erlang之上。但是,设计师最终放弃了缩进敏感性,他写这篇关于该决定的帖子包括讨论他遇到的缩进+多线块问题,以及他对Guido的设计问题/决定的认识提高: / p>
http://www.unlimitednovelty.com/2009/03/indentation-sensitivity-post-mortem.html
此外,这里有一个有趣的Python样式块建议我跑过去Guido发布了一个没有实际拍摄的响应(不确定是否有任何后续的拍摄):
答案 4 :(得分:11)
[编辑]阅读this answer.它解释了为什么多行lambda不是一个东西。
简单地说,它是unpythonic。来自Guido van Rossum的博客文章:
我发现任何解决方案都无法接受,它会在表达式的中间嵌入基于缩进的块。由于我发现语句分组的替代语法(例如大括号或开始/结束关键字)同样不可接受,这几乎使得多行lambda成为无法解决的难题。
至于其余的答案。使用单行 1 lambda或命名函数。请不要使用exec
- 我很遗憾曾建议过。
1 你会对使用一行python做些什么感到惊讶。
获取多行lambda函数的解决方法(skriticos答案的扩展):
(lambda n: (exec('global x; x=4; x=x+n'), x-2)[-1])(0)
它的作用:
Python之前简化(执行)元组的每个组件 阅读分隔符。
例如,lambda x: (functionA(), functionB(), functionC(), 0)[-1]
即使是唯一的信息,也会执行所有三个功能
使用的是列表中的最后一项(0)。
通常你不能在python中的列表或元组中分配或声明变量,但是你可以使用exec
函数(注意它总是返回:None
)。
请注意,除非您将变量声明为global
,否则它不会在exec
函数调用之外存在(这仅适用于exec
个函数{ {1}}陈述)。
例如,lambda
在没有(lambda: exec('x=5;print(x)'))()
声明的情况下正常工作。但是,global
或(lambda: (exec('x=5'), exec('print(x)')))()
不会。
请注意,所有 (lambda: (exec('x=5'), x)()
变量都存储在全局命名空间中,并在函数调用完成后继续存在。由于这个原因,这不是一个好的解决方案,如果可能的话应该避免。global
从lambda函数中的global
函数声明的变量与{{1}保持独立命名空间。 (在Python 3.3.3中测试)
元组末尾的exec
获取最后一个索引。例如,global
为[-1]
。这样做只会返回所需的输出值,而不是包含来自[1,2,3,4][-1]
函数和其他无关值的4
的整个元组。
等效多线功能:
None
避免需要多行lambda的方法:
递归:
exec
布尔是整数:
def function(n):
x = 4
x = x+n
return x-2
function(0)
迭代器:
f = lambda i: 1 if i==0 or i==1 else f(i-1)+f(i-2)
答案 5 :(得分:10)
让我向你展示一个光荣但可怕的黑客:
import types
def _obj():
return lambda: None
def LET(bindings, body, env=None):
'''Introduce local bindings.
ex: LET(('a', 1,
'b', 2),
lambda o: [o.a, o.b])
gives: [1, 2]
Bindings down the chain can depend on
the ones above them through a lambda.
ex: LET(('a', 1,
'b', lambda o: o.a + 1),
lambda o: o.b)
gives: 2
'''
if len(bindings) == 0:
return body(env)
env = env or _obj()
k, v = bindings[:2]
if isinstance(v, types.FunctionType):
v = v(env)
setattr(env, k, v)
return LET(bindings[2:], body, env)
您现在可以使用此LET
表单:
map(lambda x: LET(('y', x + 1,
'z', x - 1),
lambda o: o.y * o.z),
[1, 2, 3])
给出:[0, 3, 8]
答案 6 :(得分:6)
让我试着解决@balpha解析问题。我会在多行lamda周围使用括号。如果没有括号,则lambda定义是贪婪的。
中的lambdamap(lambda x:
y = x+1
z = x-1
y*z,
[1,2,3]))
返回一个返回(y*z, [1,2,3])
但是
map((lambda x:
y = x+1
z = x-1
y*z)
,[1,2,3]))
装置
map(func, [1,2,3])
其中func是返回y * z的多行lambda。这有用吗?
答案 7 :(得分:4)
(对于仍然对这个主题感兴趣的人。)
考虑这一点(甚至包括在“多行”lambda中的其他语句中使用语句的返回值,尽管它很难看到呕吐; - )
>>> def foo(arg):
... result = arg * 2;
... print "foo(" + str(arg) + ") called: " + str(result);
... return result;
...
>>> f = lambda a, b, state=[]: [
... state.append(foo(a)),
... state.append(foo(b)),
... state.append(foo(state[0] + state[1])),
... state[-1]
... ][-1];
>>> f(1, 2);
foo(1) called: 2
foo(2) called: 4
foo(6) called: 12
12
答案 8 :(得分:3)
我在某些项目中实践这种肮脏的技巧感到内which,这有点简单:
lambda args...:( expr1, expr2, expr3, ...,
exprN, returnExpr)[-1]
我希望您能找到一种保持pythonic的方法,但是如果您必须这样做的话,这比使用exec和操纵全局变量要容易得多。
答案 9 :(得分:2)
这是多行lambda的更有趣的实现。由于python如何使用缩进来构造代码,因此无法实现。
但幸运的是,可以使用数组和括号禁用缩进格式。
正如已经指出的那样,您可以这样编写代码:
lambda args: (expr1, expr2,... exprN)
从理论上讲,如果可以保证从左到右进行求值,那么它会起作用,但是您仍然会丢失从一个表达式传递到另一个表达式的值。
一种更冗长的方法是拥有
lambda args: [lambda1, lambda2, ..., lambdaN]
每个lambda接收前一个参数的地方。
def let(*funcs):
def wrap(args):
result = args
for func in funcs:
if not isinstance(result, tuple):
result = (result,)
result = func(*result)
return result
return wrap
此方法可让您编写有点像Lisp / scheme的内容。
所以您可以这样写:
let(lambda x, y: x+y)((1, 2))
可以使用更复杂的方法来计算斜边
lst = [(1,2), (2,3)]
result = map(let(
lambda x, y: (x**2, y**2),
lambda x, y: (x + y) ** (1/2)
), lst)
这将返回一个标量数字列表,以便可以将多个值减少为一个。
拥有很多lambda肯定不会非常有效,但是如果您受到限制,它可能是一种快速完成某项工作并将其重写为实际功能的好方法。
答案 10 :(得分:2)
让我也就不同的解决方法投入两美分。
简单的单行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)
因此,lambda绰绰有余!每当我们要进行新的分配时,我们只需添加另一个lambda并调用它。考虑以下示例:
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
显然,就lambda而言是不可能的。但是在编写了一些有用的功能之后,我们就完成了类似的情况:
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
和其他高阶函数。
解决这类问题的通用方法似乎是利用惰性求值,用不接受参数的lambda替换代码块:
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
的行为像函数一样!
总结:提到的所有内容都感觉有点不自然,而且不是Python般的美丽,这是很自然的。尽管如此,它仍然有效!并且没有任何evals
和其他度量标准,因此所有智能感知都将起作用。我也不是说您应该在任何地方都使用它。通常,您最好定义一个普通函数。我只表明没有什么是不可能的。
答案 11 :(得分:1)
关于丑陋的黑客,您总是可以结合使用exec
和常规函数来定义多行函数,如下所示:
f = exec('''
def mlambda(x, y):
d = y - x
return d * d
''', globals()) or mlambda
您可以将其包装为以下功能:
def mlambda(signature, *lines):
exec_vars = {}
exec('def mlambda' + signature + ':\n' + '\n'.join('\t' + line for line in lines), exec_vars)
return exec_vars['mlambda']
f = mlambda('(x, y)',
'd = y - x',
'return d * d')
答案 12 :(得分:1)
我从python开始,但是最明显的方式来自Javascript,是将表达式提取为函数。...
人为的例子,乘法表达式(x*2)
被提取为函数,因此我可以使用多行:
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表达式本身中做多行,但是如果有人让这个线程在寻找如何调试表达式的方法(像我一样),我认为它会帮助
答案 13 :(得分:1)
我知道这是一个老问题,但是根据记录,这里是对多行lambda
问题的一种解决方案,在该问题中,一个呼叫的结果被另一个呼叫消耗。
我希望它不是超级hacky,因为它仅基于标准库函数并且不使用dunder方法。
下面是一个简单的示例,其中我们以x = 3
开始,然后在第一行中添加1
,然后在第二行中添加2
并得到{{1} }作为输出。
6
答案 14 :(得分:0)
我只是在玩些游戏,以尝试对reduce进行字典理解,并提出这个简单的技巧:
In [1]: from functools import reduce
In [2]: reduce(lambda d, i: (i[0] < 7 and d.__setitem__(*i[::-1]), d)[-1], [{}, *{1:2, 3:4, 5:6, 7:8}.items()])
Out[3]: {2: 1, 4: 3, 6: 5}
我只是想做与本Javascript dict理解相同的事情:https://stackoverflow.com/a/11068265
答案 15 :(得分:0)
如果您的lambda函数有多行,则只需使用斜杠(\
)
示例:
mx = lambda x, y: x if x > y \
else y
print(mx(30, 20))
Output: 30
答案 16 :(得分:0)
在 Python 3.8/3.9 中有赋值表达式,所以它可以在 lambda 中使用,大大 扩展功能
例如代码
multi_string_detect<-function(x,y){
temp<-sapply(y, function(z){str_detect(x, z)})
apply(temp, 1, any)
}
multi_string_detect(lorem, c('sit', 'non')
[1] TRUE FALSE FALSE TRUE
将打印 [3, 6, 9]
答案 17 :(得分:-2)
因为lambda函数应该是单行的,因为它是函数的最简单形式an entrance, then return