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


335

我听说它说不能在Python中添加多行lambda,因为它们会在语法上与Python中的其他语法结构发生冲突。我今天在公共汽车上考虑这个问题,意识到我想不出一个与多行lambda冲突的Python结构。考虑到我非常了解该语言,这令我感到惊讶。

现在,我确定Guido有一个理由不在语言中包含多行lambda,而是出于好奇:在什么情况下包含多行lambda会导致歧义?我所听到的是正确的,还是Python不允许多行lambda的其他原因?


12
tl; dr版本:因为Python是没有{}块的惰性语言,所以为了保持一致的语法设计,不允许这样做。
安德鲁(Andrew)

11
另外:我对没有人在答案中提到这一点感到非常惊讶...您可以在Python中以\字符结束行并继续到下一行...该信息有点取代了整个问题,所以...
安德鲁·


“语法设计”
nicolas

这将需要在表达式内包含语句。如果要执行此操作,lambda则首先不需要表达式。您可以简单地def在表达式中使用语句。
chepner

Answers:


153

请看以下内容:

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

这是lambda返回(y, [1,2,3])(因此映射仅获取一个参数,从而导致错误)吗?还是返回y?还是语法错误,因为新行上的逗号放置不正确?Python如何知道您想要什么?

在括号内,缩进对于python并不重要,因此您不能明确地使用多行。

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


107
如果您想从lambda返回元组,则可以强制使用括号。海事组织,这本来应该一直被执行以防止产生此类歧义,但是哦。
mpen 2012年

26
这是一个简单的歧义,必须添加一组额外的parens来解决,parens已经存在于许多地方,例如生成器表达式被其他参数包围,在整数文字上调用方法(尽管由于函数名称不能以数字开头),当然也不能是单行lambda(可能是写在多行上的长表达式)。多行lambda与这些情况有什么特别的区别,它需要在此基础上排除它们。是真正的答案。
nmclean 2014年

3
我喜欢有如此庞大的语言如何处理它,而在某种程度上有一些深层次的原因说明为什么它很难甚至不是不可能
Nicolas

1
@nicolas简而言之就是python
javadba

我不使用lambda的原因,因此在Python中开发不足。
NoName

635

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

“但是,对于我来说,任何提议的解决方案的复杂性对我来说都是巨大的:它要求解析器(或更确切地说是词法分析器)能够在缩进敏感模式和缩进不敏感模式之间来回切换,并保持堆栈技术上可以解决所有问题(已经有一堆可以概括的缩进级别了。)但是,这些都不让我直觉,这简直就是鲁伯·戈德堡的精妙之处。”


108
为什么这不是最佳答案?正如发明人明确指出的那样,这与技术原因无关,而是设计选择。
丹·阿布拉莫夫,

13
@DanAbramov,因为OP可能多年未登录。
Falken教授的合同

7
对于那些不了解Rube Goldberg参考文献的人,请参见:en.wikipedia.org/wiki/Rube_Goldberg_Machine
fjsj 2013年

56
Guido的答案只是我希望Python不依赖缩进来定义块的另一个原因。
LS 2014年

25
我不确定是否将“直觉”称为设计选择。;)
Elliot Cameron

54

通常这很丑陋(但有时替代方法甚至更丑陋),因此一种解决方法是制作一个大括号表达式:

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

不过,它不会接受任何分配,因此您必须事先准备数据。我发现这个有用的地方是PySide包装器,有时在其中有简短的回调。编写其他成员函数将更加难看。通常,您将不需要此。

例:

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

2
我的老板只是在我们的PyQt应用程序中要求这样的事情。太棒了!
TheGerm

1
为此,我也在寻找一种使用短(但仍然是多行)lambda作为PySide UI回调的好方法。
迈克尔·伦纳德

现在,我已经看到它立即建议使用lambda argsetattr(arg, 'attr','value')颠覆“无分配...”的方法。然后是and和的短路评估or。。。是由Javascript完成的。沉入你的根,就像常春藤进入墙壁。我几乎希望我不会因为圣诞节而忘记这一点。
nigel222 '18

非常聪明-而且可读性强。现在-关于那些(丢失的..)作业 ..
javadba,

@ nigel222为什么感到as愧?python语言从根本上来说是残缺的-但是无论如何,它还是用于许多数据科学的语言。因此,我们进行调整。寻找产生副作用(通常只需打印/记录!)和赋值(通常仅足以达到中间变量!)的方法就应该由该语言很好地处理。但是甚至不支持它们(至少在遵循PEP准则的情况下)
javadba

17

一些相关链接:

一段时间以来,我一直在关注Reia的开发,该开发最初也将基于Python的基于缩进的语法与Ruby块一起使用,全部都在Erlang之上。但是,设计师最终放弃了缩进敏感性,他写的有关该决定的文章包括有关他在缩进+多行块中遇到的问题的讨论,以及他对Guido的设计问题/决定的越来越多的赞赏:

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

另外,这是一个关于Python中Ruby样式块的有趣建议,我在Guido上发布了一个响应,但没有将其实际击倒(尽管不确定是否有任何后续击落):

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


12

[编辑]阅读此答案。它解释了为什么多行lambda不是问题。

简而言之,这是不可思议的。来自Guido van Rossum的博客文章:

我发现任何在表达式中间嵌入基于缩进的块的解决方案都是不可接受的。由于我发现语句分组的替代语法(例如花括号或begin / end关键字)同样不可接受,因此,这几乎使多行lambda成为无法解决的难题。


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]



这太棒了!我想下次写Python时会用到它。我主要是Lisp和JS程序员,缺少多行lambada会很痛。这是一种实现方法。
克里斯托弗·杜马斯

7

我在一些项目中实践这种肮脏的技巧感到内which,这有点简单:

    lambda args...:( expr1, expr2, expr3, ...,
            exprN, returnExpr)[-1]

我希望您能找到一种保持pythonic的方法,但是如果您必须这样做的话,那么会比使用exec和操作globals减轻痛苦。


6

让我尝试解决@balpha解析问题。我会在多行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。那样有用吗?


1
我认为最上面的应该返回map(func, [1,2,3])而最下面的应该是错误,因为map函数没有足够的参数。代码中也有一些额外的括号。
Samy Bencherif '16

将其放入运行python2.7.13的pycharm中会产生语法错误。
simbo1905

多余的括号
Samy Bencherif

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

再次调用具有不同参数的参数时,此方法将无效,并且会导致内存泄漏,除非第一行是state.clear()因为默认参数仅在创建函数时创建一次。
马修·斯科尔菲尔德

1

您可以简单地使用斜杠(\如果您的lambda函数有多行,则)

例:

mx = lambda x, y: x if x > y \
     else y
print(mx(30, 20))

Output: 30

这个问题本身涉及到使用多个表达式而不是多个文字行。
Tomas Zubiri

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表达式本身中执行多行操作,但是如果有人得到此线程来查找如何调试该表达式(像我一样),我认为这会有所帮助


2
我为什么要这样做而不是简单地写map(multiply, [1, 2, 3])
thothal

0

关于丑陋的黑客,您始终可以使用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')

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


0

这是多行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肯定不会非常有效,但是如果您受到限制,那么它可能是快速完成某项工作并将其重写为实际函数的好方法。


-3

因为lambda函数应该是单行的,因此它是函数的最简单形式, an entrance, then return


1
不,任何编写事件驱动程序的人都会告诉您,多行lambda很重要。
Akangka
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.