5.1 函数定义与使用
5.1.1 基本语法
在Python中,定义函数的语法如下。
def 函数名([参数列表]): ''’注释’'' 函数体
其中,def是用来定义函数的关键字。定义函数时在语法上需要注意的问题主要如下。
1)不需要说明形参类型,Python解释器会根据实参的值自动推断形参类型。
2)不需要指定函数返回值类型,这由函数中return语句返回的值来确定。
3)即使该函数不需要接收任何参数,也必须保留一对空的圆括号。
4)函数头部括号后面的冒号必不可少。
5)函数体相对于def关键字必须保持一定的空格缩进。
例5-1 编写函数,计算并输出斐波那契数列中小于参数n的所有值,并调用该函数进行测试。
基本思路:每次循环时输出斐波那契数列中的一个数字,并生成下一个数字,如果某个数字大于或等于函数参数指定的数字,则结束循环。
1. def fib(n): #定义函数,括号里的n是形参 2. a, b = 1, 1 3. while a < n: 4. print(a, end=' ') 5. a, b = b, a+b #序列解包,生成数列中的下一个数字 6. 7. fib(1000) #调用函数,括号里的1000是实参
本例代码中各部分的含义如图5-1所示。
图5-1 函数定义与调用示意图
5.1.2 递归函数
如果在一个函数中直接或间接地又调用了该函数自身,叫作递归调用。函数的递归调用是函数调用的一种特殊情况,函数调用自己,自己再调用自己,自己再调用自己……当某个条件得到满足的时候就不再调用了,最后再一层一层地返回直到该函数的第一次调用,如图5-2所示。
图5-2 函数递归调用示意图
函数递归通常用来把一个大型的复杂问题层层转化为一个与原来问题本质相同但规模很小、很容易解决或描述的问题,只需要很少的代码就可以描述解决问题过程中需要的大量重复计算。在编写递归函数时,应注意以下几点。
● 每次递归应保持问题性质不变。
● 每次递归应使用更小或更简单的输入。
● 必须有一个能够直接处理而不需要再次进行递归的特殊情况来保证递归过程可以结束。
● 函数递归深度不能太大,否则会引起内存崩溃。
例5-2 编写函数,使用递归实现整数的因数分解。
基本思路:在每次递归中,查找参数num的最小因数,然后把num与该因数的整商作为参数传递给下一次递归,直到参数num没有1和自己之外的因数为止。
1. from random import randint 2. 3. def factors(num, fac=[]): 4. #每次都从2开始查找因数 5. for i in range(2, int(num**0.5)+1): 6. #找到一个因数 7. if num%i == 0: 8. fac.append(i) 9. #对商继续分解,重复这个过程 10. factors(num//i, fac) 11. #注意,这个break非常重要 12. break 13. else: 14. #不可分解了,自身也是个因数 15. fac.append(num) 16. 17. facs = [] 18. #生成随机数对上面的函数facs()进行测试 19. n = randint(2, 10**8) 20. factors(n, facs) 21. result = '*'.join(map(str, facs)) 22. if n==eval(result): 23. print(n, '= '+result)
某3次运行结果分别为:
77190905 = 5*11*41*34231 89832530 = 2*5*8983253 29134505 = 5*19*19*16141