Python学习笔记——装饰器

装饰器

笔记整理于廖雪峰官网、菜鸟教程和下面这个博客内容,这一部分和闭包我都看了好久啊,和廖雪峰相比觉得下面的博客更通俗易懂的讲解装饰器~
https://blog.youkuaiyun.com/xiangxianghehe/article/details/77170585
菜鸟教程
https://www.runoob.com/w3cnote/python-func-decorators.html
廖雪峰
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584

  • 描述
    装饰器(Decorator)可以在不更改函数代码的情况下拓展增加函数功能。
  • 使用
    定义一个带返回函数的高阶函数(使用闭包),用@语法将装饰器放置在函数定义的前一行。@作为修饰符,将被修饰的函数作为参数,返回修饰后的函数。
def log(func):
    print('log')
    def wrapper(*args, **kw):
        print('wrapper')
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

@log #相当于now=log(now)
def now():
    print('2015-3-25')

now()
#输出
#log
#wrapper
#call now():
#2015-3-25
  • 装饰器本身需要传入参数,那就需要编写一个返回decorator的高阶函数
def log(text):
    print('using log')
    def decorator(func):
        print('using decorator')
        def wrapper(*args, **kw):
            print('using wrapper')
            print('%s %s():' % (text, func.__name__))
            print('return func')
            return func(*args, **kw)
        print('return wrapper')
        return wrapper
    print('return decorator')
    return decorator

#log('execute')(now)
#首先执行log('execute')返回decorator函数,再将now作为参数传入decorator
@log('execute')
def now():
    print('2015-3-25')
    
now()
#输出
'''
using log
return decorator
using decorator
return wrapper
using wrapper
execute now():
return func
2015-3-25
'''
  • functool.wraps
    它可以把被封装函数的name、module、doc和 dict(函数名、注释文档、参数列表等)都复制到封装函数。wraps(wrapped, assigned=(‘module’, ‘name’, ‘qualname’, ‘doc’, ‘annotations’), updated=(‘dict’,))
    之前的代码中,函数now经过装饰器,返回wrapper(),now函数的对象属性now.__name__返回wrapper(),因此函数名由‘now’变成了‘wrapper’。因此需要import functools将原始函数的函数名等属性复制到wrapper中。
    利用functools中wraps函数:
import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
  • 多个装饰器执行顺序
    从最后一个装饰器开始执行,直到执行第一个装饰器,最后再执行函数本身
    1| dec1(dec2(test))
    2| 执行dec2(test): 输出aaaa return two 相当于dec1(two),two中的func被赋值test
    3| 执行dec1(two): 输出1111 return one,one中的func被赋值two
    4| 执行one:输出2222 调用嵌套函数func实际是two
    5| 执行two:输出bbbb 调用嵌套的func()也就是test,输出test test,输出cccc,
    6| 返回one 输出3333
def dec1(func):
    print("1111")
    def one():
        print("2222")
        func()
        print("3333")
    return one
 
def dec2(func):
    print("aaaa")
    def two():
        print("bbbb")
        func()
        print("cccc")
    return two
 
@dec1
@dec2
def test():
    print("test test")
 
test()
#相当于执行test=dect1(dect2(test))
#先执行dect2(test),输出aaaa,func指向函数test,return two
#执行dect1(test),输出1111,func指向函数two,return one
#实施赋值,用函数代替函数名test实际调用被装载的函数,
#test()实际上执行的是函数one,运行到func()时执行函数two,再运行到func()时执行未修饰的函数test。
  • 应用场景(这部分只是了解了一下没有深入看)
    抽离大量与函数本身功能无关的相同的代码放入装饰器来使用,为已存在的对象添加额外的功能。
    1、授权
    装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask(python编写的轻量级web应用框架)和Django web框架(开放源代码的web应用框架)中。这里是一个例子来使用基于装饰器的授权:
#Requests是Python编写的一个HTTP库
from functools import wraps 
def requires_auth(f):
    @wraps(f) 
        def decorated(*args, **kwargs): 
            auth = request.authorization 
            if not auth or not check_auth(auth.username, auth.password): 
            authenticate() 
            return f(*args, **kwargs) 
        return decorated

2、日志文件
指定一个用于输出的日志文件,输出调用的函数

from functools import wraps
 #带参数的装饰器,装饰器应用单个函数作为参数,再多一层return装饰器的函数
def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile,并写入内容‘
            #a’不存在则创建,存在则在原有内容后写入
            with open(logfile, 'a') as opened_file:
                # 现在将日志写入到指定的logfile
                opened_file.write(log_string + '\n')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator
 
@logit()
def myfunc1():
    pass
 
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
 
@logit(logfile='func2.log')
def myfunc2():
    pass
 
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串

3、类装饰器
类也可以用作装饰器,比嵌套函数的做法更整洁,具体看下面
https://www.runoob.com/w3cnote/python-func-decorators.html

  • 课后作业
    设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
import functools,time
def metric(fn):
  #  @functools.wraps(fn)
    def decorator(*args, **kw):
        starttime=time.time()
        result=fn(*args,**kw)
        endtime=time.time()
        msecs=(endtime-starttime)*1000
        print('%s executed in %d ms' % (fn.__name__,msecs))
        #return fn(*args,**kw)  开始的时候忘写这一句  
        return result#这里如果用return fn则再次调用浪费时间
    return decorator

@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('测试失败!')
else:
    print('测试成功')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值