python装饰器

本文详细介绍了Python装饰器的使用,包括无参装饰器、有参装饰器和带参数的类装饰器。通过示例展示了装饰器如何用于记录函数调用、动态添加属性、实现超时处理等功能,并解释了如何消除装饰器带来的副作用,如使用`wraps`函数装饰器。此外,还探讨了在Flask框架中使用装饰器创建路由的方法。
摘要由CSDN通过智能技术生成

1. 无参装饰器

定义:

  1. 他是一个函数
  2. 接收一个函数作为它的形参传入
  3. 返回值也是一个函数
  4. 可以使用@functionname方式进行简化调用

装饰器和高阶函数:

  1. 装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
  • 使用场景:(一是:记录函数,二是:给函数做一些特殊标记)

    def dec(func):
        def inner():
    		return func()
    	return inner
    
    
    @dec
    def f1():
        ...
        return ...
    
  • 例如:将函数调用记录下来

    recording = list()
    
    
    def dec(func):
        def inner():
            global recording
            recording.append(func)
            return func()
        return inner
    
    
    @dec
    def f1():
        pass
        return 'success'
    
    
    @dec
    def f2():
        pass
        return 'success'
    
    
    print(recording)
    resp1 = f1()
    print(recording)
    resp2 = f2()
    print(recording)
    
    []
    [<function f1 at 0x7fbe30117268>]
    [<function f1 at 0x7fbe30117268>, <function f2 at 0x7fbe2a40f488>]
    
  • 例如:给函数动态添加属性test

    def dec(func):
        def inner():
            # 动态给函数增加属性
            try:
                getattr(func, 'test')
            except AttributeError as e:
                setattr(func, 'test', True)
            print(getattr(func, 'test'))
            print("被装饰的函数:", func.__name__)
            return func()
        return inner
    
    
    @dec
    def f1():
        pass
        return 'success'
    
    
    resp = f1()
    print("==============================")
    print("装饰后返回的函数:", f1.__name__)  # 我们发现此时f1并不是原本的f1函数对象了,而是内层函数inner
    # print(getattr(f1, 'test'))  # 所以我们在装饰器内给该函数设置的属性在这里已经获取不到了,这样就背离了我们的初衷,我会在后面讲到如何处理这种情况
    print(resp)
    
    True
    被装饰的函数: f1
    ==============================
    装饰后返回的函数: inner
    success
    
  • 解决上述bug:简单示例

    def copy_wrapper(src):
        def _copy(dst):
            """用原函数中(f1)的属性覆盖目标函数(inner)相关属性
            :param dst:
            :return:
            """
            dst.__name__ = src.__name__
            dst.__doc__ = src.__doc__
            dst.__dict__ = src.__dict__
            dst.__module__ = src.__module__
            ...
            return dst
        return _copy
    
    
    def dec(func):
        """
        copy_wrapper(func)  ==> 返回 _copy 函数引用;
        @_copy def inner  ==>  _copy = _copy(inner)  ==> 返回dst(即inner)对象
        此时inner对象相关属性已经被替换成被装饰函数的相关属性
        """
        @copy_wrapper(func)
        def inner():
            ...
            return func()
        return inner
    
    @dec
    def f1():
        pass
        return 'success'
    
    resp = f1()
    print(getattr(f1, 'test'))  
    print(resp)
    
    True
    success
    
  • 对装饰器的改造python内部已经帮我们封装好了wraps函数装饰器,我们只需要调用该装饰器就可以直接实现。

  • warps 作用: 消除(被装饰后的函数名等属性的改变)副作用

    from functools import wraps
    	
    def dec(func):
        @wraps(func)
        def inner():
            # 动态给函数增加属性
            try:
                getattr(func, 'test')
            except AttributeError as e:
                setattr(func, 'test', True)
            print(getattr(func, 'test'))
            print("被装饰的函数:", func.__name__)
            return func()
        return inner
    
    @dec
    def f1():
        pass
        return 'success'
    
    
    resp = f1()  # 此时的f1对象和原函数f1对象基本上一模一样
    print(getattr(f1.__wrapped__, 'test'))
    

2. 有参装饰器

定义:

  1. 他是一个函数
  2. 接收一个函数作为参数传入
  3. 返回值是一个不带参数的装饰器函数
  4. 使用@functionname(参数列表)方式调用
  5. 可以看成是装饰器外层又加了一层函数
  • 可以根据装饰器携带参数进行任务分发

    import datetime
    
    
    def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
    	"""记录超时的函数对象,对其进行输出func参数可以根据需求进行数据库存储等操作
    	params: duration:超时时间
    	params: func:超时处理函数
    	return: func
    	"""
        def _logger(function):  # function: 被装饰函数
            @wraps(function)
            def wrapper(*args, **kwargs):  # 被装饰函数参数
                start_time = datetime.datetime.now()
                ret = function(*args, **kwargs)
                delta = (datetime.datetime.now() - start_time).total_seconds()
                if delta > duration:  # 如果调用时间超过duration,则调用func函数进行相关处理
                    func(function.__name__, duration)
                return ret
            return wrapper
        return _logger
    
    
  • 例如:flask框架中使用装饰器做路由

    def router(self, rule, **options):
        def decorator(func):  # 此处才是真正的内层装饰器函数
            def inner():
                endpoint = options.pop("endpoint", None)
                self.add_url_rule(rule, endpoint, func, **options)
                return func
            return inner
        return decorator
    

3. 被装饰函数带有参数

代码示例:

  • 这是最常见的装饰器函数之一

    def outer(func):
        def inner(*args, **kwargs):
            a, b, c = args
            if (a > 10) or (b > 10) or (c > 10):
                return False
            return func(*args, **kwargs)
        return inner
    
    
    # 方式一:
    @outer
    def my_func(a, b, c):
        return a + b + c
    
    resp = my_func(2, 5, 6)
    print(resp)
    
    
    # 方式二:
    func = outer(my_func)  # 返回inner函数引用
    resp = func(2, 5, 6)  # 两步可以精简为一步: resp = outer(my_func)(2, 5, 6)
    print(resp)
    
    13
    13
    

逻辑解析:

  1. 绑定装饰器

    @outer
    def my_func(a, b, c):
    	pass
    
    resp = my_func(2, 5, 6)
    print(resp)
    
  2. 上述绑定方式主要做了以下事件

    outer(myfunc)(a, b, c)
    
    """
    @outer
    def my_func
    相当于 myfunc = outer(myfunc)
    """
    
    """
    此处调用的my_func已经不再是我们当初的函数对象my_func,而是经过装饰器装饰过的对象
    resp = my_func(2, 5, 6)
    """
    
    
  3. 执行逻辑

    第一步:outer(myfunc) --> 返回 sub_func 函数的引用

    第二步:sub_func(a, b, c) 执行 sub_func 函数

    第三步:执行if判断

    第四步:return func(a, b, c) 执行func函数调用结果,并返回

4.带参类装饰期

In [63]: class Test:
    ...:     def check(self, *args, **kwargs):
    ...:         print(self)
    ...:         print("来自装饰器自身需要的参数:", *args)
    ...:         def outer(func):
    ...:             def inner(num):
    ...:                 print("开始调用内部方法get_test")
    ...:                 self.get_test(num)
    ...:                 print("内部方法get_test调用结束")
    ...:                 return func(num)
    ...:             return inner
    ...:         return outer
    ...:
    ...:     def get_test(self, num):
    ...:         print("接收到外部参数:%d" % num)
    ...:
    ...:

In [64]:

In [64]: test = Test()

In [65]: @test.check("------001------")
    ...: def test(args):
    ...:     resp = args + 1
    ...:     print("得到结果:", resp)
    ...:     return resp
    ...:
    ...:
<__main__.Test object at 0x00000229DDF61E80>
来自装饰器自身需要的参数: ------001------

In [66]: test(123)
开始调用内部方法get_test
接收到外部参数:123
内部方法get_test调用结束
得到结果: 124
Out[66]: 124
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值