Python学习笔记 - 装饰器

有时候我们希望给一个函数新增一些功能,但是又不想对原有函数进行修改,那么这时候就用到了装饰器或者说是装饰模式。

例子:

# 1. 有一个求和函数sum
def sum(a, b):
    return a + b
    
# 2. 如果想要知道这个函数的执行时间,如何实现?

# 实现一
import time

def sum(a, b):
    return a + b
start = time.time()
sum(5, 6)
end = time.time()

print('sum函数的执行时间大约是:%f秒' % (end - start))

# 3. 如果还有一个求差函数sub,要知道这个函数的执行时间,如何实现?
'''
按照实现获取sum函数执行时间的思路,完全可以照搬上面的代码,之前说过有重复
地方尽量封装成函数。
'''

# 实现二

import time


def print_execution_time(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print('%s函数的执行时间大约是:%f秒' % (func.__name__, end - start))
        return result
    return wrapper


def sum(a, b):
    return a + b


def sub(a, b):
    return a - b


print_execution_time(sum)(10, 20)
print_execution_time(sub)(10, 20)

复制代码

这样就实现了一个函数用于获取某个函数的执行时间, 这种模式就是装饰模式,有点像闭包,只不过传递的参数 换成了传递函数。

但是这样做有点问题,就是原本函数的调用方式改变了。而且函数主要的目的也变得 很模糊,原函数主要是用于计算,改写成这样后就好像获取执行时间才是主要目的。

Python提供了一个语法糖 - 装饰器@。通过这个可以写出更为简洁的代码。例子:

# 上面例子使用Python提供的装饰器改写:

import time


def print_execution_time(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print('%s函数的执行时间大约是:%f秒' % (func.__name__, end - start))
        return result
    return wrapper

@print_execution_time  # 新增
def sum(a, b):
    return a + b

@print_execution_time  # 新增
def sub(a, b):
    return a - b

sum(10, 10)
sub(1, 3)
复制代码

结果:

用了装饰器之后,调用和以前完全一样。只是在定义函数的时候加上@装饰函数名这种语法,原本函数就获得了装饰函数中新增的功能。

上面例子中我们都用了func.__name__,这句代码的意思是获取函数的名字。但是用了上面装饰器之后我们再看一下sum和sub函数的__name__属性:

print('sum的__name__是:%s' % (sum.__name__))
print('sub的__name__是:%s' % (sub.__name__))
复制代码

变了,全部变成wrpper了。这是因为使用@print_execution_time的时候实际上等于 sum = print_execution_time(sum),得到结果就是wrapper函数。我们可以使用funcools模块中的wrap方法来改正这个问题:

import time
from functools import wraps # 新增


def print_execution_time(func):
    @wraps(func)                  # 新增
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print('%s函数的执行时间大约是:%f秒' % (func.__name__, end - start))
        return result
    return wrapper


@print_execution_time
def sum(a, b):
    return a + b


@print_execution_time
def sub(a, b):
    return a - b


sum(10, 10)
sub(1, 3)

print('sum的__name__是:%s' % (sum.__name__))  # sum的__name__是:sum
print('sub的__name__是:%s' % (sub.__name__))  # sub的__name__是:sub
复制代码

装饰器本身也是可以带参数的,带参数的装饰器写法如下:

import time
from functools import wraps


def print_execution_time(text):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print('%s函数的执行时间大约是:%f秒' % (func.__name__, end - start))
            print(text)
            return result
        return wrapper
    return decorator 


@print_execution_time('今天天气不错')
def sum(a, b):
    return a + b


@print_execution_time('今天天气不好')
def sub(a, b):
    return a - b


sum(10, 10)
sub(1, 3)

print('sum的__name__是:%s' % (sum.__name__))
print('sub的__name__是:%s' % (sub.__name__))

复制代码

结果:

写法就是在原有的装饰器上再包裹一层函数。

@print_execution_time('今天天气不错') 相当于

sum = print_execution_time('今天天气不错')(sum)

总结下两种写法:

  1. 不带参数的装饰器
from functools import wraps


def decorator(func):
    @wraps(func)
    def wrapper(*args, **wkargs):
        # 写新增功能
        result = func(*args, **wkargs)
        return result
    return wrapper   
复制代码
  1. 带参数的装饰器
from functools import wraps


from functools import wraps
def decorator_wrapper(*args, **wkargs):
    def decorator(func):
        @wraps(func)
        def wrapper(*sub_args, **sub_wkargs):
            # 写新增功能
            result = func(*sub_args, **sub_wkargs)
            return result
        return wrapper
    return decorator
复制代码

转载于:https://juejin.im/post/5c344842f265da61715e81de

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值