Python学习笔记|装饰器

Bruce
2023-12-24 / 0 评论 / 43 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2023年12月24日,已超过333天没有更新,若内容或图片失效,请留言反馈。

装饰器(decorators)用于拓展原来函数功能的一种函数,目的是在不改变原来函数名(或类名)的情况下,给函数增加新功能。在Python中,装饰器的语法以@开头。

装饰器主要应用于:引入日志、函数执行时间统计、执行函数前的预备处理、执行函数后清理功能、权限校验、缓存。

【案例1】利用装饰器实现统计函数的执行时间

import time
def timeit(func): #func()为装饰器绑定的方法,绑定装饰器后自动传入
    def result():
        start_time = time.time()
        func()
        end_time = time.time()
        print('函数运行时间为:%.2fs'%(end_time - start_time))
    return result

@timeit
def func_0():
    time.sleep(3)

@timeit
def func_1():
    time.sleep(3)

func_0()
func_1()
函数运行时间为:3.01s
函数运行时间为:3.01s

【案例2】无参数的装饰器应用

import time
def timeit(func):
    def result():
        start_time = time.time()
        func() #调用test方法
        end_time = time.time()
        print('函数运行时间为:%.2fs'%(end_time - start_time))
    return result

@timeit
def test():
    time.sleep(1)
    print("测试函数运行")

test()
测试函数运行
函数运行时间为:1.00s

装饰器@timeit会将test()函数进行包装,实际上调用的是result()函数。在result()函数中,先调用了func(),也就是test()函数,然后再计算函数的运行时间并输出。因此,"测试函数运行"会先输出,然后才会输出"函数运行时间为:1.00s"

【案例3】有参数的装饰器应用

import time
def timeit(func):
    def result(arg1): #调用test方法的参数
        start_time = time.time()
        func(arg1) #调用test方法
        end_time = time.time()
        print('函数运行时间为:%.2fs'%(end_time - start_time))
    return result

@timeit
def test(mStr):
    time.sleep(2)
    print("函数运行时间:" + mStr)

test("传入参数")
函数运行时间:传入参数
函数运行时间为:2.00s

【案例4】定义一个打印日志的装饰器

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@log('execute')
def now():
    print('2015-3-25')

now()
execute now():
2015-3-25

上面的函数,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

函数也是对象,它有__name__等属性,但经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper':

now.__name__
'wrapper'

因为返回的wrapper()函数名字是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。下:

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps即可实现,所以,一个完整的decorator的写法如下:

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func) # wrapper()的前面加上@functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

【案例5】装饰器可以修饰带参数函数,也可以修饰不带参数函数

import time
def timeit(func):
    def result(*arg1,**kwargs): #传入非固定参数,这样即使装饰器函数不带参数也可以被装饰
        start_time = time.time()
        func(*arg1,**kwargs) #调用test方法
        end_time = time.time()
        print('函数运行时间为:%.2fs'%(end_time - start_time))
    return result

@timeit
def test():
    time.sleep(2)
    print("函数运行")

test() #调用函数
函数运行
函数运行时间为:2.02s

【例题】设计一个decorator,可作用于任何函数上,并打印该函数的执行时间:

import time, functools
def metric(fn):
    @functools.wraps(fn)
    def result(*args,**kwargs):
        start_time = time.time()
        res = fn(*args, **kwargs)
        end_time = time.time()
        print('%s executed in %.fms' % (fn.__name__, (end_time - start_time) * 1000))
        return res # 将fn(*args, **kwargs)的返回值返回给调用者
    return result
    
# 测试
@metric
def fast(x, y):
    time.sleep(0.0012)
    return x + y;

@metric
def slow(x, y, z):
    time.sleep(0.1234)
    return x * y * z;

f = fast(11, 22)
s = slow(11, 22, 33)

if f != 33:
    print('测试失败!')
elif s != 7986:
    print('测试失败!')
fast executed in 11ms
slow executed in 135ms
0

评论 (0)

取消