Python中没有多行Lambda:为什么不?
我听说不能在Python中添加多行lambda,因为它们在语法上与Python中的其他语法结构冲突。我今天在公共汽车上考虑过这个问题,意识到我想不出一个多行lambda冲突的Python构造。考虑到我对这门语言相当熟悉,这让我很惊讶 现在,我确信Guido没有在语言中包含多行lambda是有原因的,但出于好奇:在什么情况下包含多行lambda是不明确的?我听到的是真的吗,或者Python不允许多行lambda还有其他原因吗?请看以下内容:Python中没有多行Lambda:为什么不?,python,syntax,lambda,Python,Syntax,Lambda,我听说不能在Python中添加多行lambda,因为它们在语法上与Python中的其他语法结构冲突。我今天在公共汽车上考虑过这个问题,意识到我想不出一个多行lambda冲突的Python构造。考虑到我对这门语言相当熟悉,这让我很惊讶 现在,我确信Guido没有在语言中包含多行lambda是有原因的,但出于好奇:在什么情况下包含多行lambda是不明确的?我听到的是真的吗,或者Python不允许多行lambda还有其他原因吗?请看以下内容: map(multilambda x: y=x
map(multilambda x:
y=x+1
return y
, [1,2,3])
这是一个lambda返回的(y[1,2,3])
(因此map只获取一个参数,导致错误)?还是返回y
?还是因为新行的逗号放错了位置而导致语法错误?Python如何知道您想要什么
在paren中,缩进对python来说并不重要,因此您不能明确地使用多行
这只是一个简单的例子,可能还有更多的例子。Guido van Rossum(Python的发明者)亲自回答了这个问题。基本上,他承认这在理论上是可能的,但任何提议的解决方案都不是python式的: “但对我来说,任何针对这个难题提出的解决方案的复杂性都是巨大的:它要求解析器(或者更准确地说,lexer)能够在缩进敏感模式和缩进不敏感模式之间来回切换,保留一堆以前的模式和缩进级别。从技术上讲,这一切都可以解决(已经有一堆可以概括的缩进级别了)。但所有这些都不能消除我的直觉,那就是这一切都是精心设计的。”
两个相关链接: 有一段时间,我一直在关注Reia的开发,Reia最初也将在Erlang之上使用Python基于缩进的语法和Ruby块。但是,设计师最终放弃了缩进敏感性,他写的这篇关于该决定的文章包括了关于他在缩进+mu中遇到的问题的讨论lti线块,以及他对Guido设计问题/决策的日益赞赏: 另外,我在Python中遇到了一个关于Ruby风格块的有趣建议,Guido在其中发布了一个响应,而不是实际将其击落(但不确定是否有任何后续击落):
这通常非常难看(但有时备选方案甚至更难看),因此解决方法是制作大括号表达式:
lambda: (
doFoo('abc'),
doBar(123),
doBaz())
但它不会接受任何任务,所以您必须事先准备数据。
我发现这很有用的地方是PySide包装器,在那里你有时会有很短的回调。编写额外的成员函数会更难看。通常你不需要这个
例如:
pushButtonShowDialog.clicked.connect(
lambda: (
field1.clear(),
spinBox1.setValue(0),
diag.show())
mx = lambda x, y: x if x > y \
else y
print(mx(30, 20))
Output: 30
让我尝试解决@balpha解析问题。我会在多行lamda周围使用括号。如果没有括号,lambda定义是贪婪的。因此
map(lambda x:
y = x+1
z = x-1
y*z,
[1,2,3]))
返回一个函数,该函数返回(y*z[1,2,3])
但是
意味着
其中func是返回y*z的多行lambda。这行吗?[Edit]阅读它解释了为什么多行lambda不是一件事
简单地说,这是非音速的。来自Guido van Rossum的博客:
< >我发现在表达式中间嵌入一个基于压痕的块是不可接受的。因为我发现语句分组(例如括号或开始/结束关键字)的可替代语法同样不可接受,这使得多行lambda是一个不可解的难题。
(适用于仍对该主题感兴趣的任何人。)
考虑一下这一点(甚至包括在“多行”lambda中的进一步语句中使用语句的返回值,尽管这会让人恶心;-)
让我向你们介绍一个光荣但可怕的黑客:
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]
关于丑陋的黑客,您可以始终使用exec
和常规函数的组合来定义如下多行函数:
f = exec('''
def mlambda(x, y):
d = y - x
return d * d
''', globals()) or mlambda
let(lambda x, y: x+y)((1, 2))
您可以将其包装成如下函数:
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')
我对在我的一些项目中实践这种肮脏的黑客行为感到内疚,这有点简单:
lambda args...:( expr1, expr2, expr3, ...,
exprN, returnExpr)[-1]
我希望你能找到一种保持pythonic的方法,但如果你必须这样做的话,那就不会像使用exec和操纵globals那么痛苦了。我只是在玩一点游戏,试图用reduce来理解dict,并想出一行代码:
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}
[1]中的from functools import reduce
在[2]中:reduce(lambda d,i:(i[0]<7和d.uu setitem_uuu(*i[:-1]),d)[-1],{},*{1:2,3:4,5:6,7:8}.items())
Out[3]:{2:1,4:3,6:5}
我只是想做与Javascript dict理解中相同的事情:因为lambda函数应该是一行的,因为它是函数的最简单形式,
一个入口,然后返回
如果lambda函数有多行,您可以简单地使用斜杠(\
)
例如:
pushButtonShowDialog.clicked.connect(
lambda: (
field1.clear(),
spinBox1.setValue(0),
diag.show())
mx = lambda x, y: x if x > y \
else y
print(mx(30, 20))
Output: 30
我从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))
如果这是如何在lambda表达式本身中执行多行操作,那么它可能无法准确回答这个问题,但是如果有人看到这个线程正在研究如何调试表达式(像我一样),我认为这将有助于这是一个更有趣的多行lambda实现
lambda args: (expr1, expr2,... exprN)
lambda args: [lambda1, lambda2, ..., lambdaN]
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
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)
from functools import reduce
reduce(lambda data, func: func(data), [
lambda x: x + 1,
lambda x: x + 2
], 3)
## Output: 6
def f(x):
y = f1(x)
z = f2(x, y)
return y,z
f = lambda x: (lambda y: (y, f2(x,y)))(f1(x))
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)))
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
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]
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
#%%
x = 1
y = 2
q = list(map(lambda t: (tx := t*x, ty := t*y, tx+ty)[-1], [1, 2, 3]))
print(q)