Python中没有多行Lambda:为什么不呢?

我听说它说多线lambdas不能在Python中添加,因为它们会与Python中的其他语法结构在语法上发生冲突。 我今天在公交车上想到这个,意识到我想不出一个多线lambdas碰撞的Python构造。 鉴于我很熟悉这门语言,这让我感到惊讶。

现在,我确定Guido有理由不在语言中包含多行lambdaexpression式,但是出于好奇:包含多行lambdaexpression式的情况会不明确? 是我听说过的真的,还是有其他原因,Python不允许多行lambdas?

看看下面的内容:

map(multilambda x: y=x+1 return y , [1,2,3]) 

这是一个lambda返回(y, [1,2,3]) (因此地图只有一个参数,导致错误)? 还是它返回? 或者它是一个语法错误,因为新行上的逗号放错了位置? Python如何知道你想要什么?

在parens中,缩进与python无关,所以你不能毫不含糊地使用多行。

这只是一个简单的例子,可能有更多的例子。

Guido van Rossum(Python的发明者)在一篇旧的博客文章中自己回答了这个确切的问题。
基本上,他承认这在理论上是可行的,但任何提议的解决scheme都将是非pythonic:

“但是对于这个难题,任何提议的解决scheme的复杂性都是巨大的,对我来说:它需要parsing器(或者更确切地说是词法分析器)能够在缩进敏感和缩进不敏感模式之间来回切换,保持堆栈以前的模式和缩进级别,从技术上讲,这一切都可以解决(已经有一堆压缩级别可以推广),但是没有一个能够带走我的直觉,这是一个精心制作的Rube Goldberg装置 。

这通常是非常丑陋的(但有时替代品更难看),所以一个解决方法是做一个大括号expression式:

 lambda: ( doFoo('abc'), doBar(123), doBaz()) 

它不会接受任何任务,所以你必须事先准备数据。 我发现这个地方有用的地方是PySide包装,有时你有短的callback。 编写额外的成员函数将会更加难看。 通常你不会需要这个。

例:

 pushButtonShowDialog.clicked.connect( lambda: ( field1.clear(), spinBox1.setValue(0), diag.show()) 

一些相关的链接:

一段时间以来,我一直在关注Reia的开发,最初将Python语言的缩进语法与Ruby块一起,都在Erlang之上。 但是,devise师最终放弃了对缩进的敏感度,而他在这篇文章中写到的这个决定包括讨论他遇到的缩进+多行块问题,以及他为Guido的devise问题/决定所获得的更多赞赏:

http://www.unlimitednovelty.com/2009/03/indentation-sensitivity-post-mortem.html

另外,这里提供了一个Python中Ruby风格块的有趣build议,我跑过Guido发布了一个没有实际拍摄的响应(不确定是否有任何后续的拍摄):

http://tav.espians.com/ruby-style-blocks-in-python.html

让我试着解决@balphaparsing问题。 我会围绕多行lamda使用括号。 如果没有括号,lambda定义是贪婪的。 所以lambda在

 map(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。 那样有用吗?

(对于仍然对这个话题感兴趣的人)

考虑到这一点(甚至包括在“multiline”lambda中进一步的语句中使用语句的返回值,尽pipe这对呕吐来说是丑陋的;-)

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

让我向你展示一个可怕而可怕的黑客:

 import types def _obj(): return lambda: None def LET(bindings, body, env=None): '''Introduce local bindings. ex: LET(('a', 1, 'b', 2), lambda o: [oa, ob]) gives: [1, 2] Bindings down the chain can depend on the ones above them through a lambda. ex: LET(('a', 1, 'b', lambda o: oa + 1), lambda o: ob) 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: oy * oz), [1, 2, 3]) 

这给出: [0, 3, 8]

[编辑]阅读这个答案。 这解释了为什么多行lambda不是一件事情。

简而言之,它是和谐的。 从Guido van Rossum的博客文章:

我发现任何解决scheme都不可接受,在expression式的中间embedded一个基于缩进的块。 由于我发现语句分组的其他语法(例如,花括号或开始/结束关键字)同样不可接受,这使得多行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中的列表或元组中的variables,但是使用exec函数你可以(注意它总是返回: None )。

  • 请注意,除非将globalvariables声明为globalvariables,否则将不会在该exec函数调用之外存在(这仅适用于lambda语句中的exec函数)。

  • 例如, (lambda: exec('x=5;print(x)'))()没有global声明工作正常。 然而, (lambda: (exec('x=5'), exec('print(x)')))()(lambda: (exec('x=5'), x)()不会。

  • 请注意,所有globalvariables都存储在全局名称空间中,并且在函数调用完成后将继续存在。 出于这个原因,这不是一个好的解决scheme,应尽可能避免。 从lambda函数中的exec函数声明的globalvariables与global名称空间保持分离。 (在Python 3.3.3中testing)

  • 元组末尾的[-1]获取最后一个索引。 例如[1,2,3,4][-1]4 。 这样做只会返回所需的输出值,而不是包含exec函数和其他无关值的整个元组。

等效多线function:

 def function(n): x = 4 x = x+n return x-2 function(0) 

避免需要多线lambda的方法:

recursion:

 f = lambda i: 1 if i==0 or i==1 else f(i-1)+f(i-2) 

布尔值是整数:

 lambda a, b: [(0, 9), (2, 3)][a<4][b>3] 

迭代器:

 lambda x: [n**2 for n in x] #Assuming x is a list or tuple in this case