1.4 return的重要性——函数的递归调用
在定义和使用函数时,如果仅仅在函数体内部修改参数值,而不用return返回,那么在调用时就无法得到更新后的数值,甚至还会得到None。在如下的FuncBadUsage.py案例中,我们演示这一效果。
01 def addSalary(currentNum): 02 currentNum = currentNum + 1000 03 # return currentNum 04 print(addSalary(5000)) # None
在第1行里,我们通过def定义了名为addSalary的函数,它有一个名为currentNum的参数。在第2行里,给currentNum变量加1000。注释掉第3行的return语句。
当我们在第4行调用addSalary方法时,打印的结果是None,这是因为addSalary方法没有通过return返回结果。如果我们打开第3行的注释,在方法结尾用return返回结果,那么在第4行调用addSalary方法时就能看到预期的结果6000。
在一个函数内部调用本函数属于递归调用。在如下的FactorialDemo.py案例中,我们将以阶乘演示函数递归调用的做法。
01 def factorial(num): 02 if(num==1): 03 return 1 04 return num * factorial(num - 1) 05 print(factorial(3)) # 6
在第1~4行的factorial函数里,我们实现了递归调用,具体做法是:在第2行判断num是否是1,若是则返回1,否则在第4行递归调用factorial(num-1)。
在第5行里,调用factorial方法的参数是3,根据定义会递归调用3*factorial(2),而factorial(2)则会递归调用2*factorial(1),由于factorial(1)有明确的返回值1,因此递归结束,随后向上推得factorial(2)等于2、3*factorial(2)等于6,由此得到最终结果。
至此,大家应该能理解递归函数的实现方式了。虽然通过递归的写法能提升代码的可读性,但是在使用时务必注意如下两点。
第一,明确定义递归的结束条件。比如在上述案例中,通过第2行和第3行的定义,让num等于1时递归调用结束,并返回1。如果没有结束条件,就会出现无限递归的情况。
第二,计算机操作系统支持的递归层数是有限的,在使用时一定得慎重,因为递归层数过多会产生异常,从而终止程序的运行。也就是说,除非能明确得知递归的层数不会太大,否则别用递归。
为了避免因递归导致的异常,不少项目组干脆禁用递归,或者定义一个比较小的阈值,比如只有明确知道递归层数小于10才能使用。在禁用递归的情况下,我们可以通过循环来实现相同的功能,比如在如下代码里通过循环实现阶乘效果。
01 def factorialByLoop(num): 02 start = 1 03 result = 1 04 while start <= num: 05 result = result * start 06 start = start + 1 07 return result 08 print(factorialByLoop(5)) # 120