装饰器详细介绍

装饰器学习笔记

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()  # 函数调用

解释

  1. 函数定义阶段

    • @register 应用于 step1。这时 register(step1) 被调用,step1 被传递给 register 函数。
    • registerstep1 存储到 event 列表中,并返回一个新的 wrapper 函数。
    • step1 被替换为 wrapper 函数,因此原来的 step1 函数已经被装饰。
  2. 函数调用阶段

    • 当你调用 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()
      
  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
闭包装饰器是一种特殊的装饰器,它使用闭包的概念来实现。闭包是指一个函数可以访问并操作其外部函数中定义的变量。在Python中,闭包装饰器可以用于给函数添加额外的功能,同时保持函数的原始定义不变。 引用中的示例展示了装饰器传参的形式。在这个例子中,outer函数是一个装饰器,它将inner函数作为子函数返回,并在inner函数中添加了额外的功能。通过使用@outer装饰器语法,我们可以在add函数上应用outer装饰器,从而在调用add函数时执行装饰器中的代码。 引用中的示例展示了多层装饰器的使用。在这个例子中,outer1和outer2函数分别是两个装饰器,他们都返回一个inner函数。通过使用@outer1和@outer2装饰器语法,我们可以在outers函数上应用这两个装饰器,并在调用outers函数时按照装饰器的定义顺序执行相关的代码。 引用提供了关于Python闭包装饰器的使用方法的总结。这篇文章通过示例代码详细介绍了闭包装饰器的使用,对于学习和工作有一定的参考价值。 引用中的示例展示了装饰器的形式。在这个例子中,outer函数是一个装饰器,它将inner函数作为子函数返回,并在inner函数中添加了额外的功能。通过使用@outer装饰器语法,我们可以在add函数上应用outer装饰器,从而在调用add函数时执行装饰器中的代码。 综上所述,Python闭包装饰器是一种利用闭包概念实现的特殊装饰器,可以用于给函数添加额外的功能。这种装饰器可以通过装饰器传参的形式、多层装饰器的形式或普通的装饰器形式来实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值