python闭包与装饰器
函数参数
函数名的作用
- 函数名存放的是函数所在内存空间的地址,即函数名代表函数入口地址
函数名()
执行函数名所存放空间地址上的代码,即函数名()
代表函数调用,直接调用- 函数名可以像普通变量一样赋值,函数值后的结果与原函数名作用是一样的,即函数名可以做函数参数
例:定义一个无返回值的函数func01
,并直接输出函数名查看
# 定义函数
def func01():print('func01')
# 函数名() 执行函数名所存放空间地址上的代码
func01()
# 函数名存放的是函数所在空间上的地址
print(func01) # fun01函数在内存中的地址 <function func01 at 0x000001AE86427880>print(func01()) # None func01() 是调用函数,print打印的是该函数的返回值,无返回值则为None# 函数名也可以像普通变量一样赋值
func02 = func01 # 把 func01 函数的 地址值 赋值给func02,即 func02()=func01()
func02()
函数作为参数使用
函数名作为实参传递,本质上传递的是对应函数的地址
例:将函数作为实参来传递:
- 定义一个无参函数method()
- 定义一个有一个参数的函数func()
- 把无参函数method()函数名传递给有参函数func(),观察效果
def method():print('--- method函数 ---')def func(fn_name):print('--- func函数 ---')fn_name() # 执行传递进来的fn_name函数if __name__ == '__main__':print('--- 程序开始 ---')func(method) # 把method函数的内存地址传递给fn_nameprint('--- 程序结束 ---')
闭包
当调用完函数后,函数内定义的变量就销毁了,但有时候需要保存函数内的这个变量,并在这个变量的基础上完成一系列的操作
闭包语法
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,像这种使用外部函数变量的内部函数称为闭包
闭包的前提条件
- 有嵌套:即有外部函数,有内部函数
- 有引用:即在内部函数中,使用外部函数的变量
- 有返回:即在外部函数中,返回内部函数(对象)
# 外部函数
def 外部函数名(外部参数):# 内部函数def 内部函数名(内部参数):...[使用玩不函数的变量]return 内部函数名
例:定义一个函数用于保存变量10,然后调用函数返回值变量并重复累加数值
def method():a = 10return adef outer(num1):def inner(num2):sum = num1 + num2print(f'求和结果为:{sum}')return inner # 返回inner函数的内存地址,此时inner函数没有被执行if __name__ == '__main__':print('--- 程序开始 ---')# 把outer函数的内存地址赋值给fnfn = outer(10) #fn = outer函数的返回值 = inner这个内部函数对象print(fn) # 地址值print(fn(1)) # 11print(fn(1)) # 11print(fn(1)) # 11
nolocal
关键字
nolocal
用于嵌套函数中,允许内部函数修改外部函数(非全局)作用域的变量。要求变量在外层函数中已定义并赋值,且不能在内部函数中首次赋该变量值。
def outer():a = 100def inner():# nonlocal a # 声明a为外部函数的局部变量nonlocal aa += 1print(f'--- inner函数 --- a = {a}')return innerif __name__ == '__main__':print('--- 程序开始 ---')fn = outer()fn() # 101fn() # 102fn() # 103
装饰器
装饰器的作用是不改变原有函数的基础上,给原有函数增加额外功能,其本质上就是一个闭包函数
装饰器的构成条件
- 有嵌套:在函数嵌套(函数里面再定义函数) 的前提下
- 有引用:内部函数使用了外部函数的变量(还包括外部函数的参数)
- 有返回:外部函数返回了内部函数名
- 有额外功能:给需要装饰的原有函数增加了额外功能
# 1. 定义装饰器(闭包函数),用于对指定函数的功能 进行扩展
def check_login(fn_name):# 2. 定义内部函数,用于对指定的 fn_name函数 进行功能扩展def inner():print('--- 登录验证 ---')fn_name() # 调用原函数return inner # 返回内部函数的引用# 3. 定义函数
@check_login # 装饰器写法2:@装饰器名
def comment():print('--- 发布评论 ---')# 4. 调用装饰器,传入被装饰的函数,返回一个新的函数对象
if __name__ == '__main__':print('--- 程序开始 ---')# 装饰器写法1:变量= 装饰器名(要被装饰的原函数名)# hg = check_login(comment)# hg()comment() # 调用装饰器,传入被装饰的函数,返回一个新的函数对象
装饰器的使用
无参无返回值的函数
# 在无参无返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...def print_info(fn):def inner():print('正在努力计算中...')fn()return inner@print_info
def sum_num():a = 10b = 20result = a + bprint(f'计算结果是:{result}')if __name__ == '__main__':sum_num()
有参无返回值的函数
# 在有参无返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...def print_info(fn):def inner(x,y):print('正在努力计算中...')fn(x,y)return inner@print_info
def sum(x,y):z = x + yprint(f'计算结果是:{z}')if __name__ == '__main__':sum(10,20)
无参有返回值的函数
# 在无参有返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...def print_info(fn):def inner():print('正在努力计算中...')result = fn() # 调用原函数,获取结果return result # 返回结果return inner@print_info
def sum():a = 10b = 20result = a + breturn resultif __name__ == '__main__':result = sum() # 调用装饰器,传入被装饰的函数,返回一个新的函数对象print(f'计算结果是:{result}')
有参有返回值的函数
# 在有参有返回值的原有函数求和计算之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...def print_info(fn):def inner(a, b): print('正在努力计算中...')result = fn(a, b) return result return inner@print_info
def sum_num(x, y):z = x + yreturn zif __name__ == '__main__':result = sum_num(10, 20)print(f'计算结果是:{result}')
通用装饰器(函数参数不定长,有返回值)
# 定义一个可以计算多个数据和多个字典value值之和的函数,并调用。在原有函数的计算结果之前,添加一个友好提示:正在努力计算中...def print_info(func):def inner(*args, **kwargs): print("正在努力计算中...")result = func(*args, **kwargs) return resultreturn inner# *args 元组类型
# **kwargs 字典类型
def get_sum(*args, **kwargs):result = 0for value in args:result += valuefor value in kwargs.values():result += valuereturn resultif __name__ == '__main__':result = get_sum(1, 2, 3, 4, 5, a=10, b=20, c=30)print(f"计算结果是:{result}")
多个装饰器装饰一个函数
多个装饰器的装饰过程是:离函数最近的装饰器先装饰,然后外面的装饰器在进行装饰,由内到外的装置过程
例:发表评论之前,需要先登录用户,再进行校验验证码。先定义有发哦表评论的个功能函数,然后在不改变原有函数的基础上,需要先检查用户登录和输入验证码
def check_user(fn):def inner():print('输入用户名和密码')print('检查用户名和密码')print('登录成功')fn()return innerdef check_code(fn):def inner():print('校验登录验证码')fn()return inner@check_user
@check_code
def comment():print('发表评论')
if __name__ == '__main__':comment()"""
输入用户名和密码
检查用户名和密码
登录成功
校验登录验证码
发表评论
"""
带参数的装饰器
使用带参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回装饰器
def logging(flag):def decorator(fn):def inner(a,b):if flag == '+':print('正在进行加法运算')elif flag == '-':print('正在进行减法运算')result = fn(a,b)return resultreturn innerreturn decorator@logging('+')
def add(a,b):result = a+breturn result@logging('-')
def sub(a,b):result = a-breturn resultif __name__ == '__main__':add_result = add(1,2)print(f"加法计算结果是:{add_result}")sub_result = sub(5,3) print(f"减法计算结果是:{sub_result}")