Skip to the content.

Python装饰器踩坑

装饰器(Decorator),顾名思义,就是对函数的一种装饰,是AOP(面向切面编程)的一种实现方式,对函数的功能做的一些增强、拓展。主要作用就是解耦,简化代码,使代码更优雅。常见场景比如插入日志、性能测试、缓存、权限校验等。

@ 符号是装饰器的语法糖

直接见第三点。

1. 最简单的装饰器

e.g. 计算一个函数从开始到结束运行时间

import time

def time_spent(f):
    """
    :param f: function
    :return: 计算函数耗时多久
    """
    def _wrapper(*args, **kwargs):
        now = time.time()
        res = f(*args, **kwargs)
        log_txt = u'Func %s spent %.2fs!' % (f.__name__, time.time() - now)
        print(log_txt)
        return res
      
    return _wrapper
@time_spent
def demo():
    time.sleep(1)
    print("demo")
    
demo()
demo
Func demo spent 1.00s!
[Finished in 1.1s]

2. 带参数的装饰器

装饰器中可以加参数,这样可以使装饰器更加灵活。

继续使用上面的例子。我们想加个参数,用来判断是否发邮件

import time

# 发邮件
def start2send_mail():
    print("Send Mail")

def time_spent(send_mail=False):
    """
    :param send_mail: 是否发邮件
    :return: 
    """
    def outer_wrapper(f):
        """
        :param f: function
        :return: 计算函数耗时多久
        """
        def _wrapper(*args, **kwargs):
            now = time.time()
            res = f(*args, **kwargs)
            log_txt = 'Func %s spent %.2fs!' % (f.__name__, time.time() - now)
            print(log_txt)
            if send_mail:
                start2send_mail()
            return res
        return _wrapper
    return outer_wrapper
@time_spent(send_mail=True)
def demo():
    time.sleep(1)
    print("demo")
    
demo()
demo
Func demo spent 1.00s!
Send Mail
[Finished in 1.1s]

@time_spent()
def demo():
    time.sleep(1)
    print("demo")
    
demo()
demo
Func demo spent 1.00s!
[Finished in 1.1s]

3. 开始套娃(使用多层装饰器)

当然函数是可以添加多个装饰器的,你可以根据你的需要添加。

我们使用同一个装饰器试试,例子基于不带参数的装饰器。

@time_spent
@time_spent
@time_spent
def demo():
    time.sleep(1)
    print("demo")
    
demo()
demo
Func demo spent 1.00s!
Func _wrapper spent 1.00s!
Func _wrapper spent 1.00s!
[Finished in 1.1s]
from functools import wraps

def time_spent(f):
    @wraps(f) # <---------------添加------------
    def _wrapper(*args, **kwargs):
        now = time.time()
        res = f(*args, **kwargs)
        log_txt = 'Func %s spent %.2fs!' % (f.__name__, time.time() - now)
        print(log_txt)
        return res
    return _wrapper
demo
Func demo spent 1.00s!
Func demo spent 1.00s!
Func demo spent 1.00s!
[Finished in 1.1s]

Ps

  1. 示例1装饰器的等效代码:
demo = time_spent(demo)
  1. 示例2装饰器的等效代码:
demo = time_spent1(send_mail=True)(demo)