【有书共读01】《python学习手册》读书笔记十四
第十九章:函数的高级话题
当开始使用函数时,就开始面对如何将组件聚合在一起的选择了。例如,如何将任务分解成为更有针对性的函数(导致了聚合性)、函数将如何通信(耦合性)等。
待续..#笔记##读书笔记#原书内容:
几点原则:
1.耦合性:对于输入使用参数并且对于输出使用return语句
只有在真正必要的情况下使用全局变量。全局变量通常是一种蹩脚的函数间进行通信的办法。它们引发了依赖关系和计时的问题,会导致程序调试和修改的困难
2.耦合性:不要改变可变类型的参数,除非调用者希望这样做。
3.聚合性:每一个函数都应该有一个单一的、统一的目标。在设计完美的情况下,每一个函数都应该做一件事:这件事可以用一个简单说明句来总结。如果这个句子很宽泛(例如,“这个函数实现了整个程序”),或者包含了很多的排比(例如,“这个函数让员工产生并提交了一个比萨订单”),你也许就应该想想是不是要将它分解成多个更简单的函数了。
4.大小:每一个函数应该相对较小。5.耦合:避免直接改变在另一个模块文件中的变量============================================================================
递归函数
递归函数即直接或间接地调用自身已进行循环的函数。
--------------------------------------------------------------------------------------------------------------------------
用递归求和
对一个数字列表求和,可以使用内置的sum函数,或者自己编写一个更加定制化的版本:
在每一层中,这个函数都递归地调用自己来计算列表剩余的值的和,这个和随后加到前面的一项中。
>>> def mysum(L): if not L: return 0 else: return L[0] + mysum(L[1:]) >>> mysum([1,2,3,4]) 10
当列表变为空的时候,递归循环结束并返回0。
--------------------------------------------------------------------------------------------------------------------------
编码替代方案
有趣的是,我们也可以使用Python的三元if/else表达式在这里保存某些代码资产。也可以使用Python3.0的扩展序列赋值来使得第一个/其他的解包更简单
---------------------
>>> def mysum1(L):
return 0 if not L else L[0] +mysum1(L[1:])
>>> def mysum2(L):
return L[0] if len(L)==1 else L[0]+mysum2(L[1:])
>>> def mysum3(L):
first,*rest = L
return first if not rest else first+ mysum3(rest)
>>> L = [1,2,3,4]
>>> mysum1(L),mysum2(L),mysum3(L)
(10, 10, 10)
---------------------
上述三个例子中的后面两个会由于空的列表而失败,这是需要注意的地方。
但考虑到支持+的任何对象类型的序列,而不只是数字,后面两种方法更有通用性。
>>> mysum1(['a','b','c','d'])
Traceback (most recent call last):
File "<pyshell#39>", line 1, in <module>
mysum1(['a','b','c','d'])
File "<pyshell#30>", line 2, in mysum1
return 0 if not L else L[0] +mysum1(L[1:])
File "<pyshell#30>", line 2, in mysum1
return 0 if not L else L[0] +mysum1(L[1:])
File "<pyshell#30>", line 2, in mysum1
return 0 if not L else L[0] +mysum1(L[1:])
File "<pyshell#30>", line 2, in mysum1
return 0 if not L else L[0] +mysum1(L[1:])
TypeError: Can't convert 'int' object to str implicitly
>>> mysum2(['a','b','c','d'])
'abcd'
>>> mysum3(['a','b','c','d'])
'abcd'
--------------------- --------------------------------------------------------------------------------------------------------------------------
循环语句VS递归
尽管递归对于上述的求和的例子有效,但在这种环境中,它可能过于追求技巧了。实际上,递归在Python中并不是很常用,因为Python更强调像循环这样的简单的过程式语句,循环语句通过更为自然。例如,while循环常常使得事情更为具体一下:
---------------------
>>> L = [1,2,3,4,5] >>> mysum = 0 >>> while L: mysum+=L[0] L = L[1:] >>> mysum 15
更好的情况是,for循环为我们自动迭代,使得递归在大多数情况下不必使用(并且,很可能,递归在内存空间和执行时间方面效率较低)
>>> L = [1,2,3,4,5]
>>> mysum = 0
>>> for x in L:
mysum+=x
>>> mysum
15
--------------------------------------------------------------------------------------------------------------------------
处理任意结构
另一方面,递归可以要求遍历任意形状的结构。作为递归在这种环境中的应用的一个简单例子,考虑像下面这样的一个任务:计算一个嵌套的子列表结构中所有数字的总和:
[1,[2,[3,4],5],6,[7,8]]
简答的循环在这里不起作用,因为这不是一个线性迭代。嵌套的循环语句也不够用,因为子列表可能嵌套到任意的深度并且以任意的形式嵌套。相反,可以使用递归来对应这种一般性嵌套,以便顺序访问子列表:
>>> def sumtree(L):
tot=0
for x in L:
if not isinstance(x,list):
tot += x
else:
tot += sumtree(x)
return tot
>>> L=[1,[2,[3,4],5],6,[7,8]]
>>> sumtree(L)
36
>>> sumtree([1,[2,[3,[4,[5,[6,[7,[8]]]]]]]])
36
>>> sumtree([[[[[[[[1],2],3],4],5],6],7],8])
36
留意这段脚本末尾的测试案例。尽管这个例子是人为编写的,它是更大的程序的代表,例如,继承树和模块导入链可以战术类似的通用结构。
---------------------