目录
一、递归函数
递归函数是指在一个函数体内直接或间接调用自己,通常用来解决结构相似的问题。结构相似是指构成原问题的子问题与原问题在结构上相似,可以用类似的方法解决。递归在算法描述中有着不可替代的作用,很多看似十分复杂的问题,使用递归算法来描述会显得非常简洁与清晰
递归函数的效果与循环语句是一样的,而且递归能够解决的问题循环都能够解决。因为递归对程序逻辑的要求较高,建议初学者在程序开发过程中尽量使用循环来解决此类问题
1、递归函数的定义
递归函数的优点是定义简单,逻辑清晰。典型的递归函数定义格式如下:
def 递归函数名(形参列表):
if 递归出口条件:
return 返回值
else:
return 递归函数名(实参列表)
2、构成递归的条件
- 子问题与原问题是相同的问题,但是更简单
- 不能无限制地调用自身,必须有出口(停止条件)退出递归函数
- 递归必须在条件状态的引导下靠近出口
示例1:
# 定义一个函数,其返回值是自身与函数上一个返回值的和
def sum_num(n):
if n == 1: # 找出口
return 1 # 结束函数并返回一个值
result = n + sum_num(n-1) # 调用
return result
print(sum_num(4))
# 运行结果:
# sum_num(4)
# result = 4 + sum_num(3)
# result = 4 + 3 + sum_num(2)
# result = 4 + 3 + 2 + sum_num(1)
# result = 4 + 3 + 2 + 1 = 10
示例2:
# 定义一个函数,打印出n到9之间的所有整数
def func(num):
if num < 10: # 找出口
print(num,end=" ")
num += 1
func(num) # 进入下一层的方式
else:
return
func(5)
# 运行结果:-5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9
示例3:
# 使用递归函数打印任意个数的斐波拉契数列
# 斐波拉契数列:1 1 2 3 5 8 13 ……
# 此数列的规律为前两个数为1,从第三个数开始其值为前两个数之和
def fibo(n):
if n == 1 or n == 2:
fi = 1
else:
fi = fibo(n-1)+fibo(n-2) # 递归调用
return fi # 返回函数值
def main(): # 调用函数主程序
n = int(input("请输入要展示的斐波拉契数列个数:"))
for i in range(1,n+1):
print(fibo(i),end=" ") # 调用函数并打印
main()
# 运行结果:当n=5时,结果为:1 1 2 3 5
AI解释代码: 以上代码实现了斐波那契数列的生成和展示,具体解释如下: 1、定义了一个 fibo 函数,该函数接收一个整数参数 n,并返回第 n 项斐波那契数列的值。 2、在 fibo 函数中,当 n 等于 1 或 2 时,返回值为 1;否则,对 fibo(n-1) 和 fibo(n-2) 进行递归调用,得到第 n-1 和 n-2 项的值,然后将这两项值相加,得到第 n 项的值。 3、定义了一个 main 函数,该函数提示输入要展示的斐波拉契数列个数。 4、在 main 函数中,使用 for 循环遍历要展示的斐波拉契数列个数,并调用 fibo 函数打印每一项的值。 5、程序运行时,先调用 main 函数,根据输入的个数展示相应的斐波拉契数列。
3、使用递归的常见情况
- 定义本身是递归的,如阶层的计算
- 数据结构是递归的,如链表、树等
- 问题的解法是递归的,如汉诺塔问题
二、闭包与装饰器
1、闭包的概念
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个内部函数使用外部函数变量的现象称为闭包。闭指的是该嵌套函数的内部函数,包指的是内部函数在外部被引用
辅助理解:
def func1(num1,num2): # 外部函数
def func2(): # 内部函数,在函数嵌套的前提下
num3 = num1 + num2 # 内部函数使用了外部函数的变量
return num3 # 内部函数的返回值
return func2 # 外部函数返回了内部函数,或者说内部函数作为了外部函数的返回值
# 注意千万不能加(),否则就是返回内部函数的调用
闭包的作用是为了保证数据的安全。全局变量可以随意修改,数据不安全。如果放到一个函数中,数据就安全了,但是无法引用。闭包既可以引用函数里面的变量,又可以保证数据的安全性
2、构成闭包的条件
- 在函数嵌套(函数里面在定义函数)的前提下
- 内部函数使用了外部函数的变量(还包括外部函数的参数)
- 外部函数返回了内部函数的函数名
示例1:
def external():
print("我是外部函数")
def inside():
print("我是内部函数")
return inside
res = external() # 调用external函数,并将返回值赋值给res
res() # 调用res,也就是调用了inside函数
# 运行结果:我是外部函数
# 我是内部函数
AI解释代码: 以上代码定义了一个外部函数 external(),并在其中定义了一个内部函数 inside(),然后将内部函数 inside() 作为返回值返回,最后将返回值赋给 res。接着,执行 res() 即可调用内部函数 inside()。 执行 res() 的时候,由于 res 已经是内部函数 inside() 的引用,因此相当于执行内部函数 inside(),于是会打印出 "我是内部函数"。
示例2:
def external():
name = "外层函数"
def inside():
print("我是内部函数")
return name # 返回 name 变量的值
return inside # 返回内部函数 inside 的引用
res = external() # 将外层函数 external() 的返回值赋值给 res 变量
r1 = res() # 调用 res() 函数,并将返回值赋值给 r1 变量
print(r1) # 输出 r1 变量的值,即字符串 "外层函数"
# 运行结果:我是内部函数
# 外层函数
AI解释代码: 以上代码定义了一个外层函数 external(),和一个内部函数 inside()。在外层函数中,定义了一个字符串变量 name,然后返回内部函数 inside() 本身。 接着,使用 res = external() 执行外层函数,将内部函数 inside() 赋值给变量 res,相当于 res 现在是 inside 函数的引用。 然后,执行 r1 = res(),相当于调用 inside 函数,并将其返回值赋值给 r1。在 inside() 函数中,会打印一句话 "我是内部函数",然后返回变量 name 的值。 最后,执行 print(r1),输出变量 name 的值,即 "外层函数"。
3、装饰器
装饰器本质上是一个 Python 函数(其本质就是闭包),它可以在不改变其他函数的基础上增加一些额外功能。装饰器的原则是对扩展功能开放(一般是加验证),但是对修改功能关闭。装饰器使用时在主程序前加一个调用装饰器的字符“@”和装饰器函数名,这时主程序的参数就是装饰器函数的返回值
示例1:
# 定义一个函数,只要调用就可以打开机房门
def func():
print("机房门打开了")
func()
# 运行结果:机房门打开了
示例2:
# 上一个函数太过简单,如果一个公司的机房什么人都能进去的话会存在安全隐患
# 以下代码为开门函数提供了一个装饰器,作用是验证身份,如果是维修部就开门
def open(f):
def inner(emp):
if emp == "维修部":
f(emp)
else:
print("没有权限,机房门打开失败")
return inner
@open # func = open(func)
def func(emp):
print("机房门打开了")
func("人事部") # 加了装饰器后执行的就不是func本身了,而是inner("人事部")
func("维修部") # 加了装饰器后执行的就不是func本身了,而是inner("维修部")
# 运行结果:没有权限,机房门打开失败
# 机房门打开了
AI解释代码: 以上代码定义了两个函数,open和func,并使用装饰器将open函数应用到func函数上。 open函数定义了一个内部函数inner,该函数接收一个字符串参数emp。如果emp的取值是"维修部",则调用传入open函数的函数f(即func函数),否则打印“没有权限,机房门打开失败”。 func函数在被装饰器修饰后,实际上就成了inner函数,因此调用func函数实际上是调用inner函数。当传入的参数emp是"维修部"时,inner函数会调用f(emp),即调用func函数并打印“机房门打开了”。 最后,代码分别调用func("人事部")和func("维修部"),前者会打印“没有权限,机房门打开失败”,而后者会打印“机房门打开了”。
示例3:
def yanzheng(fun):
def shuru(user,pwd):
print("正在验证……")
if user == "张三" and pwd == "123456":
fun(user,pwd)
else:
print("登录失败")
return shuru
@yanzheng # 使用装饰器,将login函数作为参数传入,并重新赋值给login
def login(user,pwd):
print("登录成功")
login("张三","123456") # 调用login函数,并输入用户名和密码
login("李四","654321") # 调用login函数,并输入用户名和密码
# 运行结果: 正在验证……
# 登录成功
# 正在验证……
# 登录失败
AI解释代码: 以上代码实现了一个装饰器函数yanzheng和一个被装饰的函数login。装饰器函数yanzheng在被装饰的函数login执行之前,会先验证用户名和密码是否正确,如果正确则执行login函数,否则输出登录失败信息。 具体来说,程序先定义了一个装饰器函数yanzheng,该函数接受一个参数fun,该参数是一个函数对象,即被装饰的函数。函数yanzheng定义了一个内部函数shuru,该函数接受两个参数user和pwd,分别表示用户名和密码。在shuru函数中,首先输出“正在验证……”的信息,然后判断用户名和密码是否为“张三”和“123456”,如果是,则执行被装饰的函数fun,即执行login函数,否则输出“登录失败”的信息。 接下来,使用装饰器语法@yanzheng将login函数传入yanzheng函数中进行装饰。最后调用login函数,传入用户名和密码“张三”和“123456”,程序输出“正在验证……”和“登录成功”的信息。
三、迭代器与生成器
Python 中的生成器和迭代器是两个非常强大的工具,被用来处理大量数据或无限序列的,了解生成器和迭代器的概念和用法对于编写高效和可扩展的代码非常重要
1、迭代器
迭代器类似于 for 循环语句,是一种可以遍历集合中元素的方法,它可以逐个返回元素。迭代器使用__
iter__()
和__
next__()
方法实现迭代。__
iter__()
方法返回迭代器对象本身,而__
next__()
方法返回集合中的下一个元素
使用迭代器的前提是对象必须是字符串,列表、元组、字典和集合等可迭代的对象。它们是能够保存元素集合,是可迭代的容器,我们可以使用迭代器从中获取逐个获取元素
示例:
"""__iter__()是一种魔术方法,等同于 iter()方法,用于获取迭代器对象
__next__()是一种魔术方法,等同于 next()方法,用于从迭代器对象当中取值
"""
# 分别使用循环语句和迭代器两种方式获取list1中的值
list1 = ["张三","李四","王五","赵六"]
# 方法一
for date in list1:
print(date)
# 方法二
var1 = list1.__iter__() # 获取迭代器对象
print(var1) # 这里打印的是迭代器对象的地址
print(var1.__next__()) # 从迭代器对象当中取值一次:张三
print(var1.__next__()) # 从迭代器对象当中取值两次:李四
print(var1.__next__()) # 从迭代器对象当中取值三次:王五
print(var1.__next__()) # 从迭代器对象当中取值四次:赵六
print(var1.__next__()) # 取值次数大于值的个数就会报错
# 方法三
var2 = iter(list1) # iter()方法等同于__iter__()方法
print(var2)
print(next(var2)) # next()方法等同于__next__()方法
print(next(var2))
print(next(var2))
print(next(var2))
# 方法四
var3 = iter(list1) # 获取迭代器对象
num = 1 # 定义条件循环的开始条件
while num <= len(list1): # len()获取序列的长度
print(next(var3))
num += 1
2、生成器
生成器是一种特殊的迭代器,它可以使用 yield 语句将数据逐个返回,而不是像 return 语句一样一次性返回所有数据
每次调用生成器函数的__next__()方法时,它会执行生成器函数中的代码,直到遇到 yied 语句,然后返回 yield 后面的值并暂停执行,等待下一次调用 __next__ ()方法,而生成器在每次迭代时都会从上次停止的地方继续执行
yield 会依次将数据返回到内存当中,下一次执行的时候,上一次存放在内存当中的数据将会被释放。生成器可以用于处理大量数据,同时也能减少内存的占用
示例:
def func1(): # 定义函数
print("大家好,我是第一句话")
yield "第一句话讲完了"
print("大家好,这是我的第二句话")
yield "第二句话讲完了"
res = func1() # 创建一次生成器对象
print(next(res)) # 从生成器中第一次取值,打印前两句话
print(next(res)) # 第二次取值,打印后两句话
# 打印两次第一句话,相当于创建了两次,每次都是取的第一句话
print(func1().__next__())
print(func1().__next__())
四、lambda函数(匿名函数)
如果⼀个函数有⼀个返回值,并且只有⼀句代码,可以使⽤ lambda 函数来进行简化,但是不太推荐使用,因为条件比较苛刻,作用鸡肋
用法:
lambda 参数: 表达式(返回值)
示例1:
# 定义一个func1函数
def func1(num1, num2): # 形参
result = num1 + num2
return result
print(func1(10,20)) # 常规调用打印
# 使用lambda 函数简化上述程序,两者功能一样
fn = (lambda num1, num2: num1 + num2)(10, 20)
print(fn) # (10,20)是实参
注意:
- lambda 表达式的参数可有可⽆,函数的参数在 lambda 表达式中完全适⽤
- lambda 函数能接收任何数量的参数,但只能返回⼀个表达式的值
示例2:
# 无参数
fn = (lambda: 100)()
print(fn)
# 一个参数
fn = (lambda b: b)("hello python")
print(fn)
# 默认参数
fn = (lambda a, b, c=100: a + b + c)(10, 20)
print(fn)
# 不定长参数
fn1 = (lambda *args: args)(10, 20, 30)
print(fn1)
fn2 = (lambda **kwargs: kwargs)(name='python', age=20)
print(fn2)
# 待判断的 lambda(比大小函数)
fn = (lambda a, b: a if a > b else b)(1000, 500)
print(fn)