Ima*_*ist 299 python syntax lambda
我听说它说多行lambda不能在Python中添加,因为它们会在语法上与Python中的其他语法结构发生冲突.我今天在公共汽车上考虑这个问题,并意识到我想不出多线lambdas碰撞的单个Python构造.鉴于我非常了解这门语言,这让我感到惊讶.
现在,我确定Guido有理由不在语言中包含多行lambdas,但是出于好奇:包含多行lambda的情况会有多么模糊?我听到的是真的,还是有其他原因使Python不允许多行lambda?
Eli*_*ght 596
Guido van Rossum(Python的发明者)在一篇旧博客文章中自己回答了这个问题.
基本上,他承认这在理论上是可行的,但任何提议的解决方案都是非Pythonic:
"但是对于这个难题,任何提出的解决方案的复杂性对我来说都是巨大的:它需要解析器(或更确切地说,词法分析器)能够在缩进敏感和缩进不敏感模式之间来回切换,保持堆栈以前的模式和缩进级别.从技术上讲,这一切都可以解决(已经有一堆可以推广的缩进级别.)但这些都没有消除我的直觉,认为这是一个精心设计的Rube Goldberg装置."
bal*_*pha 141
请看以下内容:
map(multilambda x:
y=x+1
return y
, [1,2,3])
Run Code Online (Sandbox Code Playgroud)
这是一个lambda返回(y, [1,2,3])
(因此map只获取一个参数,导致错误)?还是会回归y
?或者它是语法错误,因为新行上的逗号是错误的?Python如何知道你想要什么?
在parens中,缩进与python无关,因此您无法明确地使用多行.
这只是一个简单的例子,可能还有更多例子.
Seb*_*tos 49
这通常非常难看(但有时替代方案甚至更难看),因此解决方法是制作括号表达式:
lambda: (
doFoo('abc'),
doBar(123),
doBaz())
Run Code Online (Sandbox Code Playgroud)
它不会接受任何作业,所以你必须事先准备好数据.我发现这个有用的地方是PySide包装器,你有时会有短回调.编写额外的成员函数会更加难看.通常你不需要这个.
例:
pushButtonShowDialog.clicked.connect(
lambda: (
field1.clear(),
spinBox1.setValue(0),
diag.show())
Run Code Online (Sandbox Code Playgroud)
Ano*_*non 18
几个相关链接:
有一段时间,我正在关注Reia的开发,它最初将使用Python的基于缩进的语法和Ruby块,所有这些都在Erlang之上.但是,设计师最终放弃了对缩进的敏感性,他写这篇关于该决定的帖子包括讨论他遇到的缩进+多行块问题,以及他对Guido的设计问题/决定的认识增加:
http://www.unlimitednovelty.com/2009/03/indentation-sensitivity-post-mortem.html
此外,这里有一个有趣的Ruby内容块的提议我跑过Guido发布的响应没有实际拍摄它(不知道是否有任何后续的击落):
http://tav.espians.com/ruby-style-blocks-in-python.html
Sam*_*rif 10
[编辑]阅读此答案.它解释了为什么多线lambda不是一个东西.
简单地说,它是unpythonic.来自Guido van Rossum的博文:
我发现任何解决方案都无法接受,它会在表达式的中间嵌入基于缩进的块.由于我发现语句分组的替代语法(例如大括号或开始/结束关键字)同样不可接受,这几乎使得多行lambda成为无法解决的难题.
至于其余的答案.使用单行1 lambda或命名函数.请不要使用__CODE__
- 我很遗憾曾经建议过.
1你会惊讶于你可以用一行python做什么.
获取多行lambda函数的解决方法(skriticos答案的扩展):
__PRE__
它能做什么:
Python在读取分隔符之前简化(执行)元组的每个组件.
例如,__CODE__
即使所使用的唯一信息是列表中的最后一项(0),也会执行所有三个函数.
通常你不能在python中的列表或元组中分配或声明变量,但是使用__CODE__
你可以使用的函数(注意它总是返回:) __CODE__
.
请注意,除非您声明一个变量,因为__CODE__
它不会存在于该__CODE__
函数调用之外(这仅适用__CODE__
于__CODE__
语句中的函数).
例如,__CODE__
没有__CODE__
声明就可以正常工作 但是,__CODE__
或__CODE__
不.
请注意,所有 __CODE__
变量都存储在全局命名空间中,并在函数调用完成后继续存在.因此,这不是一个好的解决方案,如果可能的话应该避免.__CODE__
从__CODE__
lambda函数内的函数声明的变量与__CODE__
命名空间保持独立.(在Python 3.3.3中测试)
将__CODE__
在元组的最后得到的最后一个索引.例如__CODE__
是__CODE__
.这样做以便仅所希望的输出值(一个或多个)被返回,而不是包含整个元组__CODE__
从__CODE__
功能和其他外来的值.
等效多线功能:
__PRE__
避免需要多行lambda的方法:
递归:
__PRE__
布尔是整数:
__PRE__
迭代器:
__PRE__
div*_*210 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)
Run Code Online (Sandbox Code Playgroud)
您现在可以使用此LET
表单:
map(lambda x: LET(('y', x + 1,
'z', x - 1),
lambda o: o.y * o.z),
[1, 2, 3])
Run Code Online (Sandbox Code Playgroud)
这使: [0, 3, 8]
hei*_*wol 10
让我也对不同的解决方法提出我的两分钱。
简单的单行 lambda 与普通函数有何不同?我只能想到缺少赋值、一些类似循环的结构(for、while)、try- except 子句……就这样吗?我们甚至还有一个三元运算符 - 酷!因此,让我们尝试解决这些问题。
这里有些人正确地指出,我们应该看看 Lisp 的let
形式,它允许本地绑定。实际上,所有非状态改变的赋值只能用 来执行let
。但每个 Lisp 程序员都知道let
form 绝对等同于调用 lambda 函数!这意味着
(let ([x_ x] [y_ y])
(do-sth-with-x-&-y x_ y_))
Run Code Online (Sandbox Code Playgroud)
是相同的
((lambda (x_ y_)
(do-sth-with-x-&-y x_ y_)) x y)
Run Code Online (Sandbox Code Playgroud)
所以 lambda 已经足够了!每当我们想要进行新的分配时,我们只需添加另一个 lambda 并调用它即可。考虑这个例子:
def f(x):
y = f1(x)
z = f2(x, y)
return y,z
Run Code Online (Sandbox Code Playgroud)
lambda 版本如下所示:
f = lambda x: (lambda y: (y, f2(x,y)))(f1(x))
Run Code Online (Sandbox Code Playgroud)
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)))
Run Code Online (Sandbox Code Playgroud)
到目前为止,一切都很好。但是如果我们必须重新分配,即改变状态怎么办?好吧,我认为只要所讨论的任务不涉及循环,我们就可以在不改变状态的情况下绝对幸福地生活。
虽然 for 循环没有直接的 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
Run Code Online (Sandbox Code Playgroud)
显然,就 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]
Run Code Online (Sandbox Code Playgroud)
当然,如果可能的话,应该始终考虑使用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
Run Code Online (Sandbox Code Playgroud)
当然,这并不是 try-except 子句的完整替代方案,但您始终可以使其更加通用。顺便说一句,通过这种方法,您甚至可以使if
行为变得像函数一样!
总结:很自然地,所提到的一切都让人感觉有点不自然并且不那么漂亮。尽管如此——它有效!并且无需任何evals
其他技巧,因此所有智能感知都将起作用。我也不是说你应该在任何地方使用它。大多数情况下,您最好定义一个普通函数。我只是表明没有什么是不可能的。
Python3.8之后,还有一种本地绑定的方法
lambda x: (
y := x + 1,
y ** 2
)[-1]
Run Code Online (Sandbox Code Playgroud)
For循环
lambda x: (
y := x ** 2,
[y := y + x for _ in range(10)],
y
)[-1]
Run Code Online (Sandbox Code Playgroud)
如果分支
lambda x: (
y := x ** 2,
x > 5 and [y := y + x for _ in range(10)],
y
)[-1]
Run Code Online (Sandbox Code Playgroud)
或者
lambda x: (
y := x ** 2,
[y := y + x for _ in range(10)] if x > 5 else None,
y
)[-1]
Run Code Online (Sandbox Code Playgroud)
While 循环
import itertools as it
lambda x: (
l := dict(y = x ** 2),
cond := lambda: l['y'] < 100,
body := lambda: l.update(y = l['y'] + x),
*it.takewhile(lambda _: cond() and (body(), True)[-1], it.count()),
l['y']
)[-1]
Run Code Online (Sandbox Code Playgroud)
或者
import itertools as it
from types import SimpleNamespace as ns
lambda x: (
l := ns(y = x ** 2),
cond := lambda: l.y < 100,
body := lambda: vars(l).update(y = l.y + x),
*it.takewhile(lambda _: cond() and (body(), True)[-1], it.count()),
l.y
)[-1]
Run Code Online (Sandbox Code Playgroud)
或者
import itertools as it
lambda x: (
y := x ** 2,
*it.takewhile(lambda t: t[0],
((
pred := y < 100,
pred and (y := y + x))
for _ in it.count())),
y
)[-1]
Run Code Online (Sandbox Code Playgroud)
让我试着解决@balpha解析问题.我会在多行lamda周围使用括号.如果没有括号,则lambda定义是贪婪的.所以lambda在
map(lambda x:
y = x+1
z = x-1
y*z,
[1,2,3]))
Run Code Online (Sandbox Code Playgroud)
返回一个返回的函数 (y*z, [1,2,3])
但
map((lambda x:
y = x+1
z = x-1
y*z)
,[1,2,3]))
Run Code Online (Sandbox Code Playgroud)
手段
map(func, [1,2,3])
Run Code Online (Sandbox Code Playgroud)
其中func是返回y*z的多行lambda.那样有用吗?
小智 6
我在某些项目中实践这种肮脏的技巧感到内which,这有点简单:
lambda args...:( expr1, expr2, expr3, ...,
exprN, returnExpr)[-1]
Run Code Online (Sandbox Code Playgroud)
我希望您能找到一种保持pythonic的方法,但是如果您必须这样做的话,那么会比使用exec和操作globals减轻痛苦。
小智 5
(对于仍然对该主题感兴趣的任何人。)
考虑一下这一点(甚至包括在“多行”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
Run Code Online (Sandbox Code Playgroud)