ste*_*fan 14 python lambda side-effects python-internals
这不是Python中lambda表达式中Assignment的重复,也就是说,我不会问如何欺骗Python在lambda表达式中赋值.
我有一些λ演算背景.考虑到以下代码,看起来Python非常愿意在lambda
表达式中执行副作用:
#!/usr/bin/python
def applyTo42(f):
return f(42)
def double(x):
return x * 2
class ContainsVal:
def __init__(self, v):
self.v = v
def store(self, v):
self.v = v
def main():
print('== functional, no side effects')
print('-- print the double of 42')
print(applyTo42(double))
print('-- print 1000 more than 42')
print(applyTo42(lambda x: x + 1000))
print('-- print c\'s value instead of 42')
c = ContainsVal(23)
print(applyTo42(lambda x: c.v))
print('== not functional, side effects')
print('-- perform IO on 42')
applyTo42(lambda x: print(x))
print('-- set c\'s value to 42')
print(c.v)
applyTo42(lambda x: c.store(x))
print(c.v)
#print('== illegal, but why?')
#print(applyTo42(lambda x: c.v = 99))
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
但如果我取消注释线
print('== illegal, but why?')
print(applyTo42(lambda x: c.v = 99))
Run Code Online (Sandbox Code Playgroud)
我去拿
SyntaxError: lambda cannot contain assignment
Run Code Online (Sandbox Code Playgroud)
为什么不? 这背后的深层原因是什么?
正如代码所示,它不能是功能意义上的"纯度".
我能想象的唯一解释是,assignemts不返回任何东西,甚至不返回None.但这听起来很蹩脚,并且很容易修复(一种方法:None如果body是一个语句,则使lambda表达式返回).
不是答案:
因为它是这样定义的(我想知道它为什么这样定义).
因为它在语法中(见上文).
def如果需要语句,请使用(我没有询问如何将语句添加到函数中).
"这会改变语法/语言/语义"如果能够提出这样一个变化的例子,以及它为什么会变坏,那么它就可以作为答案.
aba*_*ert 15
lambda存在的全部理由是它是一种表达方式.1如果你想要一些类似lambda但却是陈述的东西,那就是def.
Python表达式不能包含语句.事实上,这是该语言的基础,而Python从这个决定中获得了很多好处.这就是流量控制缩进的原因,而不是像许多其他尝试(如CoffeeScript)那样笨重.这是你可以通过略读每一行中的第一个对象来读取状态变化的原因.对于编译器和人类读者来说,这甚至是语言易于解析的部分原因.2
改变Python有一些方法可以"逃避"语句 - 表达式的差异,除非是以非常谨慎和有限的方式,将它变成一种完全不同的语言,并且不再具有导致人们选择的许多好处的语言Python首先.
改变Python以生成大多数语句表达式(比如说,Ruby)会再次将它变成一种完全不同的语言,而没有Python当前的好处.
如果Python 确实做出了这些改变中的任何一个,那么首先就不再有理由了lambda; 2,3你可以def在表达式中使用语句.
如何更改Python而不是制作赋值表达式?好吧,很明显会破坏"你可以通过略读每一行中的第一个对象来读取状态变化".虽然Guido通常关注的事实if spam=eggs是错误而不是有用的事情.
事实上,Python确实为您提供了在需要时(setattr甚至是明确地调用)来解决__setitem__问题的方法globals(),这并不意味着它应该具有直接的语法支持.很少需要的东西不值得语法糖 - 甚至对于那些不寻常的东西更加如此,它应该在实际完成时引起眉毛和/或红旗.
我不知道Guido在最初添加lambda回Python 1.0 时是否理解这一点.但它绝对lambda是Python 3.0中没有删除的原因.
2.实际上,Guido多次提出允许人类可以在头脑中运行的LL(1)解析器是语言基于语句的充分理由,以至于其他好处甚至不需要讨论.几年前我写过这篇文章,如果有人感兴趣的话.
3.如果你想知道为什么这么多语言确实有一个lambda表达式,尽管已经有def:在许多语言中,从C++到Ruby,函数不是可以传递的第一类对象,所以他们必须发明第二个一流的东西,但功能就像一个功能.在其他方面,从Smalltalk到Java,函数甚至不存在,只有方法,所以再次,他们不得不发明第二个不是方法而是像一个方法一样的东西.Python没有这些问题.
4.一些语言,如C#和JavaScript,实际上有完美的内联函数定义,但添加了一些lambda语法作为纯语法糖,使其更简洁,更少样板.这可能实际上值得在Python中进行(尽管到目前为止,每次尝试使用良好的语法都会失败),但它不会是当前的lambda语法,它几乎就像冗长一样def.
存在语法问题:赋值是一个语句,而lambda的主体只能有表达式.Python的语法就是这样设计的1.请访问https://docs.python.org/3/reference/grammar.html查看.
还有一个语义问题:每个语句返回什么?
我认为没有兴趣改变它,因为lambda是用于非常简单和简短的代码.此外,语句也允许语句序列,这对于lambdas来说是不可取的.
它也可以通过选择性地允许lambda体中的某些语句并指定语义来修复(例如,赋值返回None,或返回赋值;后者对我更有意义).但有什么好处呢?
Lambdas和功能是可以互换的.如果你真的有一个lambda体中特定语句的用例,你可以定义一个执行它的函数,并解决你的具体问题.
也许你可以创建一个语法宏来允许使用MacroPy3(我只是猜测,因为我是项目的粉丝,但我还没有时间潜入它).
例如,MacroPy允许您定义转换f[_ * _]为的宏lambda a, b: a * b,因此定义调用您定义的函数的lambda的语法不应该是不可能的.
1不改变它的一个很好的理由是它会削弱语法,因为lambda可以在表达式可以的位置.声明不应该.但这是我自己的一个非常主观的评论.