我听说在Python中不能添加多行lambdas,因为它们会在语法上与Python中的其他语法结构冲突。今天在公交车上,我一直在思考这个问题,并意识到我想不出任何一个Python构造可以与多行lambdas相冲突。考虑到我对这门语言相当熟悉,这让我很惊讶。

现在,我相信Guido没有在语言中包含多行lambda是有原因的,但出于好奇:在什么情况下,包含多行lambda会有歧义?我听说的是真的吗,还是有其他原因导致Python不允许多行lambda ?


当前回答

[编辑编辑]因为这个问题在被问到12年后,不知何故仍然活跃。我将延续每四年左右修改一次答案的传统。

首先,问题是多行lambda如何与Python冲突。公认的答案用一个简单的例子说明了如何做到这一点。几年前,我在下面链接了一个评分很高的答案,回答了“为什么它不是Python的一部分”这个问题——对于那些认为现有的“冲突”示例不足以使多行lambda无法在Python中实现的人来说,这个答案可能更令人满意。

在这个答案的前面迭代中,我讨论了如何在Python中实现多行lambda。后来我删除了这部分,因为这是一堆糟糕的做法。如果你愿意,你可以在这个答案的编辑历史中看到它。

然而,“为什么不呢?”的答案是“因为Rossum这么说”,这仍然可能是沮丧的来源。所以让我们看看它是否可以围绕用户balpha给出的反例进行设计:

map(lambda x:
        y=x+1 # <-- this line defines the outmost indent level*
        for i in range(12):
            y+=12
        return y
   , [1,2,3])

#*By convention it is always one-indent past the 'l' in lambda

至于我们的返回值,在python中是不允许的:

def f():
  return 3
, [1,2,3]

所以按照同样的逻辑,"[1,2,3]"不应该是返回值的一部分。让我们换个方式试试:

map(lambda x:
        y=x+1                    # part of lambda block
        for i in range(12):      # part of lambda block
            y+=12                # part of lambda block
        return y, [1,2,3])       # part of lambda block

这一点比较棘手,但由于lambda块有一个明确定义的开始(令牌'lambda'),但没有明确的结束,我认为作为lambda块的一部分在同一行上的任何东西也是lambda块的一部分。

人们可能会想象一些可以识别闭括号的特性,甚至可以基于封闭元素所期望的标记数量进行推断。一般来说,上面的表达式似乎不是完全不可能解析,但它可能有点挑战。

为了简化,你可以分离所有不打算成为块的一部分的字符:

map(lambda x:
        y=x+1                    # part of lambda block
        for i in range(12):      # part of lambda block
            y+=12                # part of lambda block
        return y                 # part of lambda block
, [1,2,3]) # argument separator, second argument, and closing paren for map

回到我们刚才的地方,但这一次它是明确的,因为最后一行位于lambda块的最低缩进深度后面。 单行lambda是一种特殊情况(通过在颜色后面没有立即换行来标识),其行为与现在相同。

这并不是说它一定要成为Python的一部分——但这只是一个简单的说明,在语言中做一些更改也许是可能的。

[编辑]阅读下面的答案。它解释了为什么多行不存在。

简单地说,它是非python的。Guido van Rossum在博客中写道:

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

其他回答

关于丑陋的黑客,你总是可以使用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')

以下是几个相关的连结:

有一段时间,我一直在跟踪Reia的开发,它最初也将在Erlang之上使用Python的基于缩进的语法和Ruby块。但是,设计师最终放弃了缩进敏感性,他写的这篇文章包括了他在缩进+多行块中遇到的问题的讨论,以及他对Guido的设计问题/决策的更多欣赏:

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

另外,这里有一个关于ruby风格的Python块的有趣建议,我遇到过Guido发布了一个响应,实际上没有将它击落(虽然不确定是否有任何后续的击落):

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

[编辑编辑]因为这个问题在被问到12年后,不知何故仍然活跃。我将延续每四年左右修改一次答案的传统。

首先,问题是多行lambda如何与Python冲突。公认的答案用一个简单的例子说明了如何做到这一点。几年前,我在下面链接了一个评分很高的答案,回答了“为什么它不是Python的一部分”这个问题——对于那些认为现有的“冲突”示例不足以使多行lambda无法在Python中实现的人来说,这个答案可能更令人满意。

在这个答案的前面迭代中,我讨论了如何在Python中实现多行lambda。后来我删除了这部分,因为这是一堆糟糕的做法。如果你愿意,你可以在这个答案的编辑历史中看到它。

然而,“为什么不呢?”的答案是“因为Rossum这么说”,这仍然可能是沮丧的来源。所以让我们看看它是否可以围绕用户balpha给出的反例进行设计:

map(lambda x:
        y=x+1 # <-- this line defines the outmost indent level*
        for i in range(12):
            y+=12
        return y
   , [1,2,3])

#*By convention it is always one-indent past the 'l' in lambda

至于我们的返回值,在python中是不允许的:

def f():
  return 3
, [1,2,3]

所以按照同样的逻辑,"[1,2,3]"不应该是返回值的一部分。让我们换个方式试试:

map(lambda x:
        y=x+1                    # part of lambda block
        for i in range(12):      # part of lambda block
            y+=12                # part of lambda block
        return y, [1,2,3])       # part of lambda block

这一点比较棘手,但由于lambda块有一个明确定义的开始(令牌'lambda'),但没有明确的结束,我认为作为lambda块的一部分在同一行上的任何东西也是lambda块的一部分。

人们可能会想象一些可以识别闭括号的特性,甚至可以基于封闭元素所期望的标记数量进行推断。一般来说,上面的表达式似乎不是完全不可能解析,但它可能有点挑战。

为了简化,你可以分离所有不打算成为块的一部分的字符:

map(lambda x:
        y=x+1                    # part of lambda block
        for i in range(12):      # part of lambda block
            y+=12                # part of lambda block
        return y                 # part of lambda block
, [1,2,3]) # argument separator, second argument, and closing paren for map

回到我们刚才的地方,但这一次它是明确的,因为最后一行位于lambda块的最低缩进深度后面。 单行lambda是一种特殊情况(通过在颜色后面没有立即换行来标识),其行为与现在相同。

这并不是说它一定要成为Python的一部分——但这只是一个简单的说明,在语言中做一些更改也许是可能的。

[编辑]阅读下面的答案。它解释了为什么多行不存在。

简单地说,它是非python的。Guido van Rossum在博客中写道:

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

我很内疚在我的一些更简单的项目中实践了这个肮脏的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