函数执行流程
示范代码
def foo1(b,b1=3):
print("foo1 called",b,b1)
def foo2(c):
foo3(c)
print("foo2 called",c)
def foo3(d):
print("foo3 called",d)
def main():
print("main called")
foo1(100,101)
foo2(200)
print("main ending")
示范代码执行流程
执行流程
- 全局帧中生成foo1、foo2、foo3、main函数对象
- main函数调用
- main中查找内建函数print压栈,将常量字符串压栈,调用函数,弹出栈顶
- main中全局查找函数foo1压栈,将常量100、101压栈,调用函数foo1,创建栈帧。print函数压栈,字符串和变量b、b1压栈,调用函数,弹出栈顶,返回值
- main中全局查找foo2函数压栈,将常量200压栈,调用foo2,创建栈帧。foo3函数压栈,变量c引用压栈,调用foo3,创建栈帧。foo3完成print函数调用后返回,返回值。main中foo2调用结束后弹出栈顶,main继续执行print函数调用,弹出栈顶。main函数返回。
示范代码
import dis
def foo1(b,b1=3):
print("foo1 called",b,b1)
foo2(2)
def foo2(a):
pass
print(dis.dis(foo1))
代码字节码
[python@centos7 416]$./foo.py
6 0 LOAD_GLOBAL 0 (print)
3 LOAD_CONST 1 ('foo1 called')
6 LOAD_FAST 0 (b)
9 LOAD_FAST 1 (b1)
12 CALL_FUNCTION 3 (3 positional, 0 keyword pair)
15 POP_TOP
7 16 LOAD_GLOBAL 1 (foo2)
19 LOAD_CONST 2 (2)
22 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
25 POP_TOP
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
None
递归函数
递归函数含义
- 函数直接或间接调用自身就是递归
- 递归需要有边界条件、递归前进段、递归返回段
- 递归一定要有边界条件
- 当边界条件不满足得实惠,递归前进
- 当边界条件满足的时候,递归返回
递归性能
- 循环稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果
- fib函数代码极简易懂,但是只能获得最外层的函数调用,内部递归结果都是中间结果,而且给定一个n都要进行近2n次递归,深度越深,效率越低,为了获取斐波那契数列需要外面在套一个n次的循环,效率更低了
- 递归还有深度限制,如果递归复杂,函数反复压栈,栈内存很快就溢出了
递归优化
解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以把循环看成是一种特殊的尾递归函数也是可以的,尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包涵表达式。这样编译器或者解释器就可以把尾递归优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况
递归函数总结
- 递归是一种很自然的表达,符合逻辑思维
- 递归相对运行效率低,每一次调用函数都要开辟栈帧
- 递归有深度限制,如果递归层次太深,函数反复压栈,内存很快就溢出
- 如果是有限次数的递归,可以使用递归调用,或者使用循环代替,循环代码稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果
- 绝大多数递归,都可以使用循环实现
- 即使递归代码很简洁,但是能不用则不用递归
求斐波那契数列
斐波那契数列解法1
- 重点内容是f(n)=f(n-1)+f(n-2),因此得出函数为
#!/home/python/.pyenv/versions/3.5.3/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def f(n):
return 1 if n < 3 else f(n-1)+f(n-2)
for i in range(1,11):
print(f(i),end=" ")
print()
运行结果:
[python@centos7 416]$./recusion.py
1 1 2 3 5 8 13 21 34 55
- 利用for循环将正序输出斐波那契数列公式
斐波那契数列解法2
#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def f(n,x=[1],y=[0]):
if n > 1:
x[0],y[0] = x[0]+y[0],x[0]
print(y[0],end=" ")
else:
return x[0]
return f(n-1,x=[x[0]],y=[y[0]])
print(f(10))
运行结果:
[python@centos7 416]$./recusion.py
1 1 2 3 5 8 13 21 34 55
- 利用函数默认值属性
- 利用函数体就是关系式,return就是返回结果进行计算
斐波那契数列解法3
#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def fib(n,cur=1,pre=0):
if n > 1:
cur,pre = cur+pre,cur
print(pre,end=" ")
else:
return cur
return fib(n-1,cur,pre) #通过位置变量传参
print(fib(10))
求n的阶乘
传统解法
#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def f(n):
if n == 1:
return 1
return n*f(n-1)
print(f(5))
运行结果:
[python@centos7 416]$./fact.py
120
for循环思路解法
#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-15
import dis
def f(n,s=[1]):
if n ==1:
print(s[0]) #顶层栈帧pop时,s[0]的值120
return
s[0] *= n
f(n-1,s=[s[0]])
return s[0] #此时最底层栈帧时,s[0]的值为5
print(f(5))
运行结果:
[python@centos7 414]$./n.py
120
None
- 此案例能够帮助更深层次理解
尾递归法
#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-16
def f(n,num=1):
if n == 1:
return num
num *= n
return f(n-1,num) #每次重新创建新的栈,而且这个栈就调用一次即可
print(f(5))
运行结果:
120
序列逆放
将一个数逆序放入列表中,如:1234=>[4,3,2,1]
#!/bin/python3
#-*- coding: UTF-8 -*-
#created time 2018-04-17
def rever(n,s,l=[]):
if n == 0:
return l
num=divmod(int(s),10)
l.append(num[1])
return rever(n-1,num[0])
print(rever(4,1234))
运行结果:
[python@centos7 416]$./lst.py
[4, 3, 2, 1]