函数与作用域
全局变量与局部变量
全局变量(Global Variable) 自赋值定义开始,后续代码都可以访问该变量;
局部变量(Local Variable) 只能在被定义的函数(后续还有类)内部被访问。
t = 7 # t为全局变量,在函数外定义赋值
def sum2(i): # i为局部变量,在函数内定义赋值
i += t # t全部变量可以被函数内部访问
return i # 返回的变量i是局部变量的值
i = 9 # i在函数外面,非函数内部的i
print('j值为%d,是全局变量;i值为%d,是局部变量'%(t, sum2(3)))
# j值为7,是全局变量;i值为10,是局部变量
print('函数外面的i值为%d,是新的变量,而非函数内的i'%(i))
# 函数外面的i值为9,是新的变量,而非函数内的i
global关键字
函数内部默认只能读取全局变量的值,若需要修改全局变量,则需要使用 global 关键字进行事先声明,否则在函数内修改全局变量会报错。
t = 7
print('全局变量t的地址为:%d'%(id(t)))
f = 4
def sum3():
global t
t += 2
f = 5
return f
print('局部变量f值为%d,全局变量f值为%d'%(sum3(), f))
print('全局变量t的值为%d,在函数里赋值后的地址为%d'%(t, id(t)))
# 全局变量t的地址为:140712352969168
# 局部变量f值为5,全局变量f值为4
# 全局变量t的值为9,在函数里赋值后的地址为140712352969232
注: 这里仅对global关键字的使用起演示作用,平时不鼓励采用该方法传递值及修改值。
闭包(Closure)
闭包是介于全局变量和局部变量之间的一种特殊变量。
t = 5 # 全局变量t
def sum4(): # 外部函数sum4
k = 2 # 闭包变量k
def sum5(): # 嵌套的内部函数sum5
i = t + k # 局部变量i
return i
return sum5()
print('调用sum4结果%d'%(sum4()))
# 调用sum4结果7
三种变量使用范围关系:
全局变量>闭包变量>局部变量。闭包变量定义位置在外部函数与内部嵌套函数之间。
nonlocal关键字
要在sum5函数中修改闭包变量k,则需要事先用nonlocal关键字声明变量k,才能对它进行修改。修改的结果k也变成函数sum5的局部变量,不鼓励使用该方法传递值及修改值。
匿名函数
Python使用 lambda 来创建匿名函数。所谓匿名函数,与用def关键字定义函数相比,没有函数名称。
匿名函数定义及特征
lambda[para1, para2,...]: expression
使用特点:
- lambda后没有跟函数名,这就是匿名函数的由来。
- [para1, para2,…]参数是可选的,任何类型的,参数往往在后面的expression体现。
- expression表达式实现匿名函数功能的过程,并返回操作结果,具有通常函数return的功能。
- 整个匿名函数在一行内实现所有定义。
lambda x, y: x * y # 在同一行定义匿名函数
# <function <lambda> at 0x00000199594BA168>
a = lambda x, y: x * y # 定义匿名函数并赋值给a
print(a(2, 3)) # a具有匿名函数的功能,通过参数传值
# 6
递归函数
什么是递归
递归(Recursion Algorithm,递归算法) 在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。
递归的算法思想:
- “重复”,既凡是通过循环语句可实现的,都可以通过递归来实现。
- “将问题分解为同类的子问题”,如持续循环的运算操作、持续循环的判断操作,它们的每次循环都是同样的一个“动作”,这个“动作”就是一个子问题。
使用递归函数
求1, 2,3,…,n加法和
def recursion_sum(num): # 定义递归函数
if num == 1: # 分解到最小数1
return num # 返回最小分解数1给上一层
return recursion_sum(num - 1) + num # 自己调用自己,重复动作:两个相邻数相加
print(recursion_sum(4)) # 调用递归函数并打印结果
# 10 # 1、2、3、4的累加和为10
对应的一般循环实现过程如下:
def sum1(num):
i = 1
add = 0
while i <= num:
add += i
i += 1
return add
print(sum1(4))
# 10
递归函数在内存中的运行原理
实现思想:
递归一次,在内存中开辟一个新的地址空间,记录递归过程状态,一直递归分解到最小范围,最后得出要么找到对应值,要么返回找不到的结果。
def recursion_sum(num):
if num == 1:
return num
t = recursion_sum(num - 1) + num
print('第%d次递归'%(num)) # 用print跟踪递归函数
print('返回值%d在内存中地址:%d'%(t, id(t)))# 跟踪递归过程变量地址变化
return t
print(recursion_sum(4))
# 第2次递归 # num = 2
# 返回值3在内存中地址:140712368828752 # recursion_sum(2- 1) + 2 =3
# 第3次递归 # num = 3
# 返回值6在内存中地址:140712368828848 # recursion_sum(3- 1) + 3 =6
# 第4次递归 # num = 4
# 返回值10在内存中地址:140712368828976 # recursion_sum(4- 1) + 4 =10
# 10
- 每递归一次,代码在内存中开辟新的保存运算过程的地址空间,记录运算过程。
- 递归算法其实分 缩小范围和求值结果得层层返回两大步骤。其实是在调用 栈(stack) 的进栈、出栈操作过程。每递归调用自己一次,就进栈一次,并在栈列表里记录调用的内容;每返回一次,就是出栈弹出值得过程,并把值返回到上一个列表里,最终返回所求最终答案。
使用递归函数得优缺点
- 优点
递归是通过有限陈述来定义无限得对象集合的可能性,即使这个程序没有明确的重复,也可以用有限递归程序来描述无限次的计算。可以对任何大小集合的整数求累加;又如二分法查找,不同的查找值查找过程次数不确定,但采用递归方法,可以灵活、有效的解决这些问题。 - 缺点
当所计算的对象数量变得庞大起来时,内存空间压力就会增大。容易使内存空间崩溃。