前面在介绍函数的时候,有一些部分跳过了,那就是 lambda expression。它的存在就像是一个小工具,有它的存在很多时候一些不大不小的问题就可以很好的得到解决方案,使用得当的话,可以有效的简化编程任务。
匿名函数:lambda
这是一个除了 def 方法外,创造一个能被后续调用的函数的另一种方式,并且常常是在“一行”内进行的函数而定义的形式被使用,或者用来推迟执行一些程式码运行,运行起来的时候,功能和 def 是基本上一样的,但是 lambda 有自己的都有之处:
- 是一个表达式,不是一个语句,因此 lambda 能够出现在 def 出现不了的地方,如列表中,并且可以选择给最后的结果赋值给谁,而非 def 那样只能返回给函数名。
- lambda 不是一个代码块,它就是简单的把结果写成一个流畅的表达式,而非明确的返回,也因为本身的表达式特性,它的功能比较小
下面是同样的函数功能与不同的写法
>>> def func(x, y, z):
a = x + y + z
return a
>>> func(2, 3, 4)
9
>>> F = lambda x, y, z: x + y + z
>>> F(2, 3, 4)
9
就作用域来说,他们两个也是独立的,代码里面的变量都遵守相同的查找法则,lambda 也可以用它自身表达式的特性,放在 def 里面使用:
>>> def Kt():
title = 'sir'
action = (lambda x: title + ' ' + x)
return action
>>> act = Kt()
>>> act('robin')
'sir robin'
# if we go even further here...
>>> ACT = (lambda x: (lambda y: x + y))
>>> act = ACT(99)
>>> act(3)
102
# and further... we even get rid off the object name like this
>>> ((lambda x: (lambda y: x + y))(99))(3)
102
使用 lambda 的理由
是一种快速建立函数的方法,总是可以用很简洁的一行话就把函数创建好,并且这行话可以放在各种地方(因其表达式的特性),当然如果较真也总能用 def 去替代它们,但是 lambda 快捷的便利性也常让 list 或是 dictionary 跟它合作,成为一个行为跳转表:
>>> L = [(lambda x: x**2), (lambda x: x**3), (lambda x: x**4)]
>>> for f in L:
print(f(2))
4 8 16
>>> print(L[0](3))
9 # we can still use "def" to achieve the same functionality shown above
# but it would be a long paragraph comparing to lambda expression here
同样的判断式 if 这类的简单语句也可以使用在 lambda 中:
>>> lower = (lambda x, y: x if x< y else y)
>>> lower('bb', 'aa')
'aa'
>>> lower('aa', 'bb')
'aa'
map 函数:程序对列表和其他序列很常要做的动作就是把其内部的个别元素分别代入 function 中操作,然后最后输出一个集合的结果。
>>> counters = [1, 2, 3, 4]
>>> update = []
>>> for x in counters:
update.append(x + 10)
>>> update
[11, 12, 13, 14]
# this pack of code can be replaced in "def" way
>>> def inc(x):
return x + 10
>>> map(inc, counters)
[11, 12, 13, 14]
# this looks neat a little bit, but we can further cut it down by lambda expression
>>> map((lambda x: x + 3), counters)
[4, 5, 6, 7]
映射:列表解析是用任意表达式去处理每个元素,并最后产生结果收集到一个新的列表中返回,map 的方法其实某种程度上和 list 里面的回圈执行有点重复了,代码如下,功能相同:
>>> [x**2 for x in range(10)]
>>> map((lambda x: x**2), range(10))
[0, 1, 4, 9, 16, 25, 49, 64, 81]
其实这还可以用一系列判断式去增加需求。不过整体来说 map 的执行速度会比较快,因为底层执行的时候是以 C 为基础的,而非 PVM 中步进运行。
生成器 Generator
普通的函数收到输入的参数后,就能够马上回传一个结果,并且可以稍后再从刚刚离开的地方继续返回值,因为其随时间生成一个序列的值
生成器函数生成值后能够自动挂起,并暂停它的执行和状态,计算过程中可能会遇到的问题里是个很好的解决方案,因为信息在函数回复的时候将会再次生效。
最大的不同就是生成器 yield 一个值,而非 return。yield 暂停被挂起的时候可以自动保存当下阶段的值,并且在下次启动的时候,从当时暂停的点开始不间断,return 也可以出现在生成器中,但是就是用来终止产生值用的,而非暂停功能。下面范例:
>>> def generator(N):
for i in range(N):
yield i**2
>>> for j in generator(5):
print(j, ':')
0:1:4:9:16
>>> generator(5)
<generator object at 0x0086C378>
当生成器遇到一系列的值时,他返回的会是一个 class object,需要用 for 回圈一个一个打印开来。
生成器最大的优势就是能够对内存空间配置的优化,整体上非常有效的提升计算机处理速度,在占用资源最小的情况下完成任务。
Principle of designing a function
- skill to separate the mission into some more specific functions
- communication between each functions and even .py files
- size control of the individual function
耦合性
对于输入,使用参数,对于输出,试用 return 语句。要力求函数内的内容独立于外部的东西,而“参数”和“return语句”就是最好的隔离办法,从而让代码中只剩下少量重点。
只有真正必要的时候才启用全局变量 global variable,因为它其实是种“插一句话”的写法,很容易引发不必要的覆盖或是其他副作用,导致调试和修改的困难,因此过来人建议就直接在文件作用域赋值是最好的,也最清晰。
不要改变可变函数的参数,除非调用者希望如此,跟 global variable 不要随便使用的原因差不多,人家内置函数本来的功能要是我们随意修改了,说不定到时候做方案跟别人写出来的 函数连用,问题会让我们多到抓狂。
最后就是文件之间,避免改变别人文件里面的变量名,那既不礼貌又不高效,问题等不听话的人亲测体悟了。
聚合性
每个函数应该都要只有一个单一且统一的目标。完美设计下,每个函数都应该做一个可以简单总结的事,如果一个 function 的共嗯很宽泛,那就要考虑更加细分内部功能
每个函数应该相对较小,判定方式就是如果内容在编译器里面要翻页才能看完,那就建议分开功能了。python 一个过长或是有着深层嵌套的方式写出来的代码,往往成为错误的源头。
从内部来讲,python 会将每个默认参数保存成一个对象,附加在这个函数本身,因为默认参数实在 def 时被评估的:
>>> def saver(x=[]):
x.append(1)
print(x)
>>> saver([2])
[2, 1]
>>> saver()
[2, 1, 1]
>>> saver()
[2, 1, 1, 1]
>>> saver()
[2, 1, 1, 1, 1]
例子中一定程度上工作起来就如同全局变量,但是变量名对于函数还是本地变量,必须认清。return 是一个让值能够回传,并且被保留下来的重要过程,要是没有这个环节,function 就像在执行一个不需要有效的计算结果一样做了个白工。