2009-08-05 70 views
194

我听说过,多行lambda不能在Python中添加,因为它们会在Python中与其他语法构造语句冲突。我今天在公共汽车上想到了这一点,并意识到我无法想象多线lambdas碰撞的单个Python构造。鉴于我非常了解这门语言,这让我感到惊讶。Python中没有多行Lambda:为什么不呢?

现在,我确定Guido有没有在语言中包含多行lambda表达式的原因,但出于好奇:包括多行lambda表达式的含义是什么?我听说过的是真的,还是有其他原因,Python不允许多行lambda表达式?

+0

** TL;博士版本:**,因为Python是一种懒惰的语言,而不{}块和所以这是不允许的,以保持一致的语法设计。 – Andrew 2017-08-11 15:02:48

+2

**另外:我很吃惊,没有人在答案中提到过这一点......你可以用Python中的\角色结束行并继续到下一行......这些信息可以取代整个问题,所以...... ** – Andrew 2017-08-11 16:48:45

回答

104

请看下面:

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

这是返回拉姆达(y, [1,2,3])(因此映射只得到一个参数,从而导致错误)?或者它返回y?或者它是语法错误,因为新行上的逗号放错了位置? Python如何知道你想要什么?

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

这只是一个简单的例子。

+58

如果你想从lambda返回一个元组,他们可以强制使用圆括号。国际海事组织,这应该始终强制执行,以防止这种含糊不清,但哦。 – mpen 2012-07-28 23:28:13

+12

这是一个简单的模糊性,必须通过添加一组额外的parens来解决,这些parens已经存在于很多地方,例如,由其他参数包围的生成器表达式,在整数文本上调用一个方法(尽管这不一定是这种情况,因为函数名不能以数字开头),当然也包括单行lambdas(可能是长表达式写在多行)。多线lambda与这些案件没有特别的区别,因此在此基础上保证排除他们。 [这](http://stackoverflow.com/a/1233520/933416)是真正的答案。 – nmclean 2014-03-07 13:41:04

435

Guido van Rossum(Python的发明者)自己在an old blog post中自己回答了这个问题。
基本上,他承认,它在理论上是可能的,但是,任何建议的解决办法是联合国Python化:

“但对于这个难题提出的任何解决方案的复杂性是巨大的,对我说:它要求解析器(或者更准确地说,词法分析器)能够在缩进敏感模式和缩进不敏感模式之间来回切换,保持以前模式和缩进级别的堆栈。从技术上讲,这一切都可以解决(已经有一堆缩进级别可以被概括),但是没有一个能够带走我的直觉,认为它全部是精心制作的Rube Goldberg contraption。“

+66

为什么这不是最佳答案?这不是关于技术原因,而是发明人明确指出的设计选择。 – 2011-11-15 21:33:39

+7

@DanAbramov因为OP可能没有登录多年。 – 2013-01-10 08:26:53

+7

对于那些不了解Rube Goldberg参考文献的人,请参阅:http://en.wikipedia.org/wiki/Rube_Goldberg_Machine – fjsj 2013-02-09 22:06:12

15

几个相关链接:

有一段时间,我在下面的区域环境影响评价,这是最初将拥有Python的基于缩进使用Ruby块语法也都在二郎山顶的发展。但是,设计师最终放弃了缩进灵敏度,他撰写关于该决定的文章包括讨论他遇到的缩进+多行块问题,以及他为Guido的设计问题/决定获得的增加的欣赏:

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

而且,这里是在蟒蛇红宝石风情的街区一个有趣的建议,我碰到哪里圭多张贴W A响应跑/ O实际拍摄下来(不知道是否有任何后续拍下来,虽然) :

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

+1

+1:感谢您的链接。 – jfs 2009-12-07 00:07:09

30

这通常是非常难看(但有时替代品就更难看了),所以解决方法是做一个牙套表达:

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

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

例子:

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

我的老板在我们的PyQt应用程序中只是要求这样的事情。真棒! – TheGerm 2015-01-26 23:55:15

+0

感谢你们,我也在寻找一种很好的方式来使用简短(但仍然是多行)的lambda作为PySide UI的回调。 – 2016-11-18 03:19:44

6

让我试图解决@balpha解析问题。我会在多行lamda中使用括号。如果没有括号,则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是多拉姆达返回Ÿ* Z。那样有用吗?

+1

我在想,顶层应该返回'map(func,[1,2,3])',底层应该是一个错误,因为map函数没有足够的参数。代码中还有一些额外的括号。 – 2016-12-01 20:28:02

4

[编辑]读取this answer.它解释了为什么多线lambda不是一件事。

简而言之,它是不刺激的。来自Guido van Rossum的博客文章:

我发现任何解决方案都不可接受,它将基于缩进的块嵌入表达式的中间。由于我发现语句分组的其他语法(例如花括号或开始/结束关键字)同样不可接受,这使得多行lambda成为难以解决的难题。

至于这个答案的其余部分。可以使用单行的 lambda或命名函数。请不要使用exec - 我很遗憾曾经暗示过。

你会惊讶你可以用一行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或Python中使用exec函数(注意它始终返回:None)分配给变量或声明变量。

  • 需要注意的是,除非你将变量声明为global它不会对exec函数调用的外部存在(这是内lambda陈述仅适用于exec功能真)。

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

  • 请注意,所有 global变量都存储在全局名称空间中,并且在函数调用完成后将继续存在。出于这个原因,这不是一个好的解决方案,应尽可能地避免。 global在lambda函数内部从exec函数声明的变量与global命名空间保持独立。 (在Python 3.3.3中测试)

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

等效多线功能:

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) 

布尔是整数:

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

任何想法为什么你的代码抛出语法错误? http://imgur.com/a/pWuH1 – 2016-11-30 23:00:14

+1

@AndrewAnthonyGerst该代码似乎只能在Python 3中运行。请参阅此处运行:https://repl.it/Ed67/0 'exec'从声明变成了声明在Python 2中成为Python 3中的一个函数。(很像'print')。这里有一个SO Q/A的更多细节:http://stackoverflow.com/questions/15086040/behavior-of-exec-function-in-python-2-and-python-3 – 2016-12-01 20:17:01

+3

有没有一种方法来讨厌答案不止一次? – khajvah 2017-04-27 08:32:56

4

(对于任何人的话题仍然有兴趣)

考虑这个(甚至包括了‘多行’拉姆达内进一步的陈述的返回值的使用,尽管它的丑陋呕吐;-)

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

让我给你介绍一个光荣的,但可怕的黑客的点:

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]

+0

最初张贴在https://gist.github.com/divs1210/d218d4b747b08751b2a232260321cdeb – divs1210 2017-06-23 20:14:18

+0

这真棒!我想我会在下次编写Python时使用它。我主要是一个Lisp和JS程序员,并且缺乏多行lambda损害。这是获得该方法的一种方式。 – 2017-08-02 20:58:41

相关问题