AberSheeran
Aber Sheeran

Python 匿名函数的妙用

起笔自
所属文集: 杂记
共计 929 个字符
落笔于

在设计 MíngShé 的字典解构赋值语法时,我遇到了一个问题,在半年之后的今天终于想到了如何解决。

字典解构赋值语法右侧的表达式是十分自由且无法控制的,它可以是一个简单的变量名,也可以是一个复杂的管道和偏函数运算结果,有的时候其计算成本十分的高。作为一个编程语言,肯定不能愚蠢地重复计算右侧的值。那么如何保存变量的值且不把作用域扩散到单条语句外?

由于当时已经写完了空值合并语法,我思维上出现了明显的路径依赖,不停在想如何使用 := 绑定右侧对象,但始终没有想出来。直到昨天漫无目的的刷新着 Telegram Group 我突然想到了 lambda 可以完成这一点。

x, y = (lambda **kwargs: (kwargs.get('x'), kwargs.get('y')))(**any_mapping)

匿名函数的参数可以在整个匿名函数内自由使用任意次、作用域也不会扩散到 lambda 之外,在匿名函数执行完成后对对象的引用会被消除,不会干扰到引用计数垃圾回收机制对对象的回收。并且匿名函数没有名称不需要担心它本身覆盖掉可能存在的同名变量。

这也可以用来优化空值合并语法和可选链语法的编译结果。以前的编译结果里,变量的作用域并不仅限于单条语句里,在用户代码本身就有 _1 之类的变量名的情况下会出现错误。通过 lambda 的优化可以解决这个问题。

(a0 + a1) ?? b 为例,以前会编译到 _1 if _1 := (a0 + a1) is not None else b,通过 lambda 改造会编译到如下代码,但语法性能上相较之前下降了九成。

(lambda arg0, arg1: _ if (_ := arg0()) is not None else arg1())(lambda : a0 + a1, lambda : b)

可以注意到这里我并没有直接把 a0 + a1b 直接作为参数,而是使用 lambda 包装了一次。函数被调用之前会先把所有参数计算出来,如果直接传递,则空值合并语法丧失了短路求值的能力。使用 lambda 包装一层就可以做到需要时才进行计算。

如果你觉得本文值得,不妨赏杯茶
URL Query Parameters
Windows 同时访问内外网