Python闭包与装饰器:代码封装的魔法

Python闭包与装饰器:代码封装的魔法

1.闭包

在函数中,函数结束后,函数内定义的变量就销毁了,但有时需要保存函数内的变量,并在此变量基础上再完成一系列操作(相当于延长函数内变量的生命周期)

而使用外部函数变量的内部函数就称之为闭包

闭包可以保存函数内的变量,使得变量不会随着调用函数而被销毁

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数

构造条件:

1.有函数嵌套

2.内部函数引用外部函数参数

3.外部函数返回内部函数名

#外部函数

Def 外部函数名(外部参数):

#内部函数

Def 内部函数(内部参数):

使用外部函数的参数

return 内部函数名

函数名和函数名()是两个概念,前者表示函数对象 后者表示调用函数 获取返回值

例如:

def add(num1:int): def wrapper(num2:int): return num1+num2 return wrapper f = add(3) # add(3) 调用函数对象add 传入参数num1 = 3 同时返回了 wrapper # 用f 接收了 wrapper 这个函数对象 print(f) # 所以单独打印f的结果为 <function add.<locals>.wrapper at 0x000001A6A305B320> 表示一个对象函数 对象名 为 wrapper # 此时f = wrapper # f(2) = wrapper(2) print(f(2)) # -> 等同于 wrapper(2) # 而调用wrapper(2) 执行 num1+num2 -> 3+2 结果为5

如果整体写 f = add(3)(2) 输入结果也是5,逐步拆解跟上面是一样的

在内存地址的角度思考:

有两个函数add(num1) 假如地址值为 0x001 和 函数wrapper(num2) 假如地址值为 0x002

而 f = add(3) 调用add函数 返回了wrapper

结果为 f = wrapper f指向了 wrapper的地址0x002

2.装饰器

作用:不修改原来函数代码的基础上,对该函数的功能进行扩展(扩展有限,只对函数开始之前和结束之后进行扩展)

应用场景:

1. 打印日志信息(例如:包含调用函数的函数名,传递了哪些参数,调用时间,运行时间等)

2. 权限控制: 例如用户登录,判断用户拥有哪些权限

3.缓存

2.1 自定义装饰器

构造条件:

1.有函数嵌套

2.内部函数引用外部函数参数

3.外部函数返回内部函数名

4.有额外功能

装饰器就是对闭包的升级

例如:

def decorate(func): def wrapper(*args,**kwargs): print("计算开始...") func() print("计算结束") return wrapper def add(): print(f'10 + 20 ={ 10 + 20}') add_wrapper = decorate(add) add_wrapper() # 没有改变add 原函数 但是对add增加了一些东西

输出结果为:

计算开始...
10 + 20 =30
计算结束

语法糖写法:

在要修饰的函数上 添加 @装饰器名称

装饰器名称就是 decorate [自定义装饰器的最外部函数名]

def decorate(func): def wrapper(*args,**kwargs): print("计算开始...") func() print("计算结束") return wrapper @decorate def add(): print(f'10 + 20 ={ 10 + 20}') add() # 调用add函数

输出结果:

计算开始...
10 + 20 =30
计算结束


可能单个函数看上去觉得这样写小题大做了
但是如果你有很多计算一类的函数 ,都想在函数执行之前输出'计算开始' 和 函数结束后输出 '计算 结束',就要一个个添加, 但写个装饰器 就不用重复写了

2.2 有参无返回值

上面2.1中的介绍都是无参无返回值的写法

# 定义装饰函数 def decorate(func): # 定义内部函数 def mrapper(num1, num2): # 添加新功能 print(f"正在计算...") # 调用外部餐宿 func(num1, num2) # 返回内部函数 return mrapper # 定义被装饰函数 @decorate def sum2(num1, num2): print(f"num1 + num2 = {num1 + num2}") num1 = int(input("请输入被加数")) num2 = int(input("请输入被数")) # 测试 sum2(num1, num2)

2.3 无参有返回值的函数

#定义装饰函数 def decorate(func): # 定义内部函数实现函数嵌套 def inner(): # 有新增功能 print("正在计算....") # 调用外部参数 return func() # 返回内部函数 return inner #定义被装饰函数 @decorate def sum3(): a = 10 b = 20 return a + b # 测试 sum3 = sum3() print(sum3)

2.4 有参有返回值的函数

# 定义装饰函数 def decorate(func): # 定义内部函数 有嵌套函数 def marpper(num1,num2): # 有参数 # 新增功能 print("正在计算") #调用外部函数 return func(num1,num2) # 且有返回值 # 返回内部函数 return marpper # 定义被装饰函数 # @decorate def sum4(num1, num2): # 有参数有返回值 return num1 + num2 sum4 = sum4(5,1) print(sum4) # 传统方式 sum4 = decorate(sum4) sum = sum4(5,1) print(sum)

2.5 可变参数函数

def decorate(func): def mapper(*args, **kwargs): print("正在努力计算....") return func(*args, **kwargs) return mapper #定义原函数 @decorate def get_sum(*args,**kwargs): return sum(args) + sum(kwargs.values()) sum = get_sum(1,2,3,4,a = 5,b = 6) print(sum)

我认识的一位著名的名人说过,如果不理解没关系,记公式

不管有没有参数,就记可变参数函数这个样子,记住了多用就理解了

2.6 多个装饰器装饰一个函数

多个装饰器装饰一个函数时,是按照由内而外的顺序装饰的

但如果使用装饰器来写,看到的效果是从上往下执行

# 多个装饰器装饰同一个函数 def check_login(func): def inner(): print("请先登录") func() return inner def check_code(func): def inner(): print("校验成功") func() return inner @check_login # 从上往下执行 @check_code # 从上往下执行 def comment(): print("发表评论") comment() # 传统方式 comment = check_code(comment) # 由内而外 comment = check_login(comment) # 由内而外 comment()

2.7 带参数的装饰器

使用带有参数的装饰器,其实就是在装饰器外面又包裹了一个函数,使用该函数接受参数,返回装饰器一个装饰器的参数只能有一个,如果装饰器有多个参数,可以在改装饰器的外边再包裹一层,把该装饰器当作内部函数返回即可

# 定义一个既能装饰减法,又能装饰减法的装饰器 # 定义装饰函数 def loging(flag): def decorate(func): # 定义内部函数 1.有函数嵌套 def wrapper(num1,num2): if flag == '+': print("正在努力进行加法运算中") elif flag == '-': print("正在努力进行减法运算中") return func(num1,num2) return wrapper return decorate #定义原函数减法 @loging('-') def reduce(num1,num2): return num1-num2 # 定义原函数加法 @loging('+') def sum(num1,num2): return num1+num2 # 测试 reduece = reduce(10,5) print(reduece) sum= sum(10,5) print(sum)

个人理解就这么点,没有画图如果有画图指向的话 执行流程会更好