文章目录
装饰器学习笔记
1.1首先需要了解什么是闭包函数?
1.1.1 基础
闭包函数组成: 一个外部函数,一个内部函数,外部函数定义的变量。
闭包函数定义:内部函数使用了外部函数定义的变量,而外部函数又返回了内部函数的对象(名称),这样就构成了一个闭包函数。
tips: 外部函数返回的对象实际上除了内部函数以外,还包含外部函数的变量作用域,++即内部函数可以使用外部函数定义的变量++
def decorator(var):
def warpper():
print("before")
print(var)
print("after")
return var # 内部函数返回外部函数定义的变量
return warpper
test_ = decorator(1)
print(test_) # test_ 相当于内部函数warpper,然后内部函数里面带有外部函数定义的自由变量var
test_() # 相当于执行 warpper(), 这个warpper就可以使用var=1这个外部函数的变量,这就意味着可以内部函数可以返回外部函数定义的变量
1.1.1 进阶
了解到闭包函数以后,就要思考一个问题,如果把外部函数定义的变量换成一个函数对象会怎么样?
++即内部函数可以使用外部函数定义的变量++ 就会转变为 ++内部函数可以使用外部函数定义的方法对象++ ,我们只需要在方法对象后面加上(),就可以当成一个方法执行了,同时内部函数也可以返回该方法的执行结果。
作用:
def test(*args, **kwargs):
print(args, kwargs)
def decorator(var):
def warpper(*args, **kwargs):
print('执行函数前的操作')
result = var(*args, **kwargs)
print('执行函数后的操作')
return result
return warpper
# 如果var等于函数test会怎么样?
warpper = decorator(test)
warpper(1, 2, 3, 4, 5, x=6, y=7)
# 执行结果直接等价于
print('执行函数前的操作')
result = test(1, 2, 3, 4, 5, x=6, y=7)
print('执行函数后的操作')
闭包函数可以通过在原始函数的前后加上一些操作来扩展函数的功能
缺点:项目里面使用这个原始方法的所有地方都需要一一修改,用处局限
所以装饰器语法糖就由此产生了
2 什么是装饰器函数?
- tips:@函数1 修饰一个函数2时,实际就等于把被修饰的函数2的名称 当成参数给到函数1。
def decorator_test(x):
return x
@decorator_test
def fun1(): # ===⇒ 等价于 decorator_test(fun1) ==>
print(1)
# decorator_test也是一个装饰器函数,但是对于func1完全由
- 装饰器函数是一个能够接受一个函数作为参数并返回一个新函数的函数。
- 闭包函数是指在函数内部定义函数,并且内部函数可以访问外部函数的局部变量。虽然许多装饰器函数都是闭包函数,但这并不是必须的。
2.1 装饰器语法糖
装饰器语法糖可以是对闭包函数的一种应用方式
优点:在不改变原有项目代码编写的情况下,只在原始方法定义的位置上面添加一行@++装饰器名称++(外部函数名称),就可达到扩展原始方法的作用。
def decorator(var):
def warpper(*args, **kwargs):
print('执行函数前的操作')
result = var(*args, **kwargs) # 内部函数执行外部函数定义的函数运行结果
print('执行函数后的操作')
return result # 内部函数返回外部函数定义函数的执行结果
return warpper
@decorator # ==> 等价于decorator(test)(*args, **kwargs)
def test(*args, **kwargs):
print(args, kwargs)
test(1, 2, 3, 4, 5, x=6, y=7)
"""
执行结果:
执行函数前的操作
(1, 2, 3, 4, 5) {'x': 6, 'y': 7}
执行函数后的操作
"""
2.2 带参数的装饰器
def decorator(dic):
def wrapper(func):
def wrapper_2(*args, **kwargs):
result = func(*args, **kwargs)
return result
return wrapper_2
return wrapper
@decorator(flag)
def func1():
print(1)
# 等价于
"""
带参数
@decorator({"test":"1"})
def func(*args, **kwargs)
....
==> 等价于 decorator({"test":"1"})(func)(*args, **kwargs)
"""
2.3 多重装饰器
from functools import wraps
def decorator1(func):
print('111111111111')
@wraps(func)
def decorator2(*args, **kwargs):
print('22222222222')
result = func(*args, **kwargs)
return result
return decorator2
def decorator3(func):
print('33333333333')
@wraps(func)
def decorator4(*args, **kwargs):
print('444444444444444')
result = func(*args, **kwargs)
return result
return decorator4
@decorator3
@decorator1
def func1(a=1):
print(a)
return a
"""
@decorator3
@decorator1
def func1(a=1):
print(a)
return a
等价于
@decorator3
decorator1(func1)(a=1)
等价于
decorator3(decorator1(func1))(a=1)
因为
decorator1(func1) ==》 decorator2 #decorator2里面的func就等于func1
所以
decorator3(decorator(func1))(a=1) ==》 decorator3(decorator2)(a=1)
因为
decorator3(decorator2) ==》 decorator4 #decorator4里面的func就等于decorator2
所以
decorator3(decorator2)(a=1) ==> decorator4(a=1)
最终:
执行方法
decorator4(a=1)
==> print('444444444444444')
==> result = decorator2(a=1)
===> print('22222222222')
===> result = func(a=1)
===> print(a)
===> return result
==> return result
"""
3. 装饰器实现原理
- 在 Python 中,函数定义时和函数调用时是两个不同的阶段,每个阶段的作用和行为都有所不同。下面是对这两个阶段的详细解释:
3.1. 函数定义时
函数定义是指在代码中声明一个函数并为其指定行为的过程。在这个阶段,Python 解释器创建了一个函数对象,并将其绑定到一个名字(函数名)上。函数定义时的操作包括:
- 函数体的编译:解释器会编译函数体中的代码,将其转换为字节码。
- 函数对象的创建:函数体被包装成一个函数对象,该对象包含了函数的代码以及相关的信息(如参数列表、文档字符串等)。
- 函数名的绑定:函数名(例如
step1
)与函数对象关联,使得以后可以通过函数名来引用这个函数。
示例:
def step1():
print("Hello, World!")
- 在这段代码中,
def step1():
定义了一个函数step1
。 - 在定义时,Python 创建了
step1
的函数对象,并将其绑定到名字step1
上。
3.2. 函数调用时
函数调用是指在代码中实际执行函数的过程。在这个阶段,Python 解释器会执行函数体中的代码。函数调用时的操作包括:
- 参数传递:将实际参数(实参)传递给函数的形式参数(形参)。
- 函数体执行:执行函数体中的代码,按照函数定义的逻辑进行处理。
- 返回值:函数执行完毕后,返回结果(如果有的话)。
示例:
step1() # 函数调用
- 在这段代码中,
step1()
是对step1
函数的调用。 - 解释器会查找
step1
的函数对象,并执行函数体中的代码(打印 “Hello, World!”)。
3.3 结合装饰器的理解
在使用装饰器时,函数定义和函数调用的区别变得更明显。装饰器在函数定义时被应用,但函数调用时才会执行装饰器的逻辑。下面是如何运作的示例:
event = []
def register(func):
event.append(func) # 将被装饰的函数存储到 event 列表中
def wrapper():
print("Before calling the function.")
func() # 调用原函数
print("After calling the function.")
return wrapper # 返回装饰后的函数
@register
def step1():
print("Inside step1.")
step1() # 函数调用
解释:
-
函数定义阶段:
@register
应用于step1
。这时register(step1)
被调用,step1
被传递给register
函数。register
将step1
存储到event
列表中,并返回一个新的wrapper
函数。step1
被替换为wrapper
函数,因此原来的step1
函数已经被装饰。
-
函数调用阶段:
- 当你调用
step1()
时,实际调用的是wrapper
函数。 wrapper
函数在执行时会打印额外的内容,并调用原始的step1
函数。
- 当你调用
3.4.总结
-
函数定义时:Python 创建函数对象并绑定到函数名上;这是函数的静态部分,即代码被解释和准备好。
-
函数调用时:Python 执行函数体中的代码;这是函数的动态部分,即代码实际运行的过程。
-
这两个阶段在 Python 中各自有其重要的作用,并通过装饰器等机制将它们结合起来以实现更复杂的功能。
4. 实例
- 实例1
- 这个装饰器的主要作用是:
- 收集函数:通过装饰器将函数收集到一个列表中。
- 统一管理:将所有被装饰的函数集中管理,存储在一个列表中。
- 批量执行:能够在需要时统一调用这些收集到的函数。
- 这种模式在事件处理、插件系统、回调函数管理等场景中非常有用,可以简化函数的注册和调用流程。
- 可以很方便地添加新函数,只需在新函数上应用装饰器即可,无需修改原有代码结构。
- 这个装饰器的主要作用是:
from typing import Callable
class RegisterEvent:
def __init__(self):
self.event = []
def register_event(self, func) -> Callable:
self.event.append(func)
return func
re = RegisterEvent()
class Compare:
@re.register_event
def _first_00(self):
pass
@re.register_event
def _first_01(self):
pass
@re.register_event
def _first_02(self):
pass
def run(self):
for i in re.event:
i(self)
if __name__ == '__main__':
print(re.event)
Compare().run()