开门见山,直接从一个例子开始,假设我们在写一个GUI界面,界面上有个按钮,其功能是点击它就能得到物品A的详细描述,对应的代码如下
detailButton.clicked.connect(self.showDetail)
其中,showDetail是一个自己写的回调函数,用来完成详细描述的展示。
这段代码看起来很平常,但问题在于,如果有新的需求,比如,出现了物品B,B的详细描述可能与A只在一个细节D有所不同(也即是说,A.D=x,B.D=y,且x≠y),但这个不同会导致B和A对应的showDetail的代码不太一样。
一种方法是,写两个完成详情展示的函数:showDetailA() 和 showDetailB(),然后看具体情况来选择到底调用哪一个。显然,这种方法会造成代码冗余——showDetailA() 和 showDetailB() 除了细节D之外,其余部分都相同,这是典型的代码臭味,可能会给后期的代码维护和修改造成不必要的麻烦。而且,如果后面需求变化,出现了物品C,C.D = z的话我们还得重新再写一个showDetailC()……总之,这样做,既笨重又危险。
如果能将D作为参数传入showDetail就好了……
但问题在于,在代码“detailButton.clicked.connect(self.showDetail)”中并没有提供给showDetail传参的位置,怎么办呢?
这时候,我们就可先以重构showDetail,使其接收参数D,根据D值来区分展示A还是展示B;然后祭出法宝lambda来解决问题。
要展示A时,代码如下(注意x是A.D的值):
detailButton.clicked.connect(lambda: self.showDetail(x))
要展示B时,代码如下(注意y是B.D的值):
detailButton.clicked.connect(lambda: self.showDetail(y))
如果我们还要展示物品C,而C.D = z,就只需要写
detailButton.clicked.connect(lambda: self.showDetail(z))
这样一来,一次重构就可以使showDetail能够被反复重用,每次用时只要传对参数即可。
小结一下。
lambda的意义其实有很多地方都讲过了(比如可以参见这篇lambda表达式),下面我们专门针对本文的这种情况来给一个直观一点的解释:
lambda为我们做了一个 “函数打包” 的操作——它把函数和它的参数列表打包在一起,得到一个新的函数(比如,对于物品A来说,就是打包了showDetail和x;对于物品B来说,就是打包了showDetail和y)。我们实际传给detailButton.clicked.connect的并不是原来的showDetail,而是打包得到的新函数。至于这个新函数叫做什么名字,对于我们来说是无所谓的(匿名函数),反正也只用这一次,下次还要用的话,重新让lambda打包就好啦。