递归函数不是帝龟啊!:一个函数调用了它自己本身就叫做递归

定义一个比较糟糕的函数调用自身:

python条件及递归 教案设计 python递归问题_递归

你会发现它调用test()的时候,它唯一的函数内容是打印myoffer然后returntest(),再回到定义函数,打印myoffer,然后return test()一直循环往复;

这就有疑问了

是不是会一直执行下去呢,理论上它会一直执行下去直到消耗掉所有内存,其实Python3设置了递归默认深度是100层,到达之后就会报错。

不过在写爬虫工具的时候就可能不止100层了,所以我们就要自力更生了。

python条件及递归 教案设计 python递归问题_python条件及递归 教案设计_02

这样深层次的调用就会导致“栈溢出”就好比一个杯子它有自己的最大容量,超过了这个容量就会溢出。

来个人道点的例子解释帝龟和栈溢出:求1-100的阶乘。

先分析:

想要得到的结果1×2×3×…×100

可以拆分成100×函数(99)【函数(99)可以完成1×2×3×…×99 】

可以拆分成 99×函数(98)【函数(99)可以完成1×2×3×…×98】

……

……

…….

可以拆分成2×函数(1)【函数(1)可以完成1×2】

正确的解法如下

python条件及递归 教案设计 python递归问题_函数调用_03

现在来了解一下这只龟,先拿一个print(test(3))来剖析

python条件及递归 教案设计 python递归问题_栈溢出_04

第一次程序执行时会先传入3,替换所有numbers,当执行到test(3-1)又要调用test()然后就会传入2,变为test(numbers=2)下面的程序。

python条件及递归 教案设计 python递归问题_python递归函数例题_05

同理执行到test(2-1)又要再次调用test(),变为test(numbers=1)下面的程序

python条件及递归 教案设计 python递归问题_栈溢出_06

直接执行else后的程序返回1

注意:一个函数调用return有两种含义:返回一个值或结束当前函数;

所以说当return 1时第二个程序中的test(2-1)就会替换为return 2×1

python条件及递归 教案设计 python递归问题_python条件及递归 教案设计_07

接着就会执行这个return 2×1时,第一个程序中的test(3-1)就会替换为return 3×2×1

python条件及递归 教案设计 python递归问题_python递归函数例题_08

当执行这个return 3×2×1时,程序就会执行结果print(test(3)),同理要执行print(test(100))也是一样的原理,这就是帝龟。

好啦现在理解了帝龟就可以更透彻明白栈溢出了

第一步当程序执行print(test(3))这玩意的时候,就要调用函数test(number=3),然后执行下面这个函数

python条件及递归 教案设计 python递归问题_栈溢出_04

执行好后呢,先把这个3放到杯子底下,然后执行numbers=2,就把2再放到杯子里,一层层上叠(就是在内存中存储好,为了当返回执行return2×1时,能够识别3×test(3-1)并替换为return 3×2×1,其它的类似,当返回执行return1时,能够识别2×test(2-1)并替换为return 2×1)

所以说每次调用自己函数的时候呢就要在杯子中装点东西,当你调用循环的次数是1000000或者更大,杯子总有满的时候,等到装不下的时候就叫做“栈溢出”了。

至于为甚么叫“栈(stack)溢出”怎么上档次的名字呢

上面说了存东西的时候都是从杯底往上一层层的叠加(调用一次函数,栈就会加一层栈帧),到你要取的时候只能先从上面拿了(函数返回栈就会减一层栈帧),杯子是有最大容量的(栈的大小不是无限的,递归调用过多就会栈溢出)总结:这种存储特性“先进后出”

注意:为什么上面的例子numbers=1要单独处理呢,因为当numbers传入1时,就会变成test(1-1),没有到0的阶乘,这样结果就会变成0.