python 基础-装饰器

目录

前言

传统做法

函数作为参数传递

 装饰器

 带参装饰器

带定参数的装饰器

带有不定参数的装饰器

 多个装饰器

functools.wraps 

 装饰器类

装饰器使用

授权(Authorization)

日志(Logging)


前言

python装饰器是一种特殊的函数,其返回值是一个函数,在不更改原函数代码前提下给原函数增加新的功能。

传统做法

定义以下原函数,以增加该函数的计时功能为例。

import time
def func():
    print("hello",end=" ")
    time.sleep(1)
    print("world!")

如果想记录下这个函数执行的总时间,那最简单直接的做法就是修改原函数。

import time
def func():
    startTime = time.time()  
    print("hello",end="  ")
    time.sleep(1)
    print("world!")
    endTime = time.time()    
    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)
func()
hello
world
time is 1001 ms

函数作为参数传递

如果要记录多个不同函数的总的执行时间就需要一个个修改,测试结束可能又不需要时间记录功能,又要改回。这样不仅增加了工作量,也增加的出错的可能性。

把要计时的函数(func)作为参数传递给计时函数(deco),也可以实现不修改原函数,增加计时功能。

#避免直接侵入原函数修改,但是生效需要再次执行函数
import time

def deco(func):
    startTime = time.time()
    func()
    endTime = time.time()
    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)


def func():
    print("hello")
    time.sleep(1)
    print("world")

deco(func)#只有把func()作为参数,新加入功能才会生效
hello
world
time is 1004 ms

 原函数功能未变

func()

 hello world!

 装饰器

以上方法需要在调用函数的位置修改函数调用为计时函数调,通过计时函数间接调用原函数,同样不需要计时功能时又要改回。 

如何做到既不需要修改原函数,也不需要修改函数的调用方法,很简单的增加函数的功能--装饰器。

import time

def deco(func0):
    def wrapper():  #函数中定义函数
        startTime = time.time()
        func0()
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper   #返回函数

@deco
def func():
    print("hello",end=" ")
    time.sleep(1)
    print("world!")
func()
hello  world!
time is 1004 ms

 带参装饰器

 需要时在函数定义前加上装饰器,不需要时去掉装饰器,而且根据需要还可以加多个装饰器,装饰器可以是带参(可以是定参、不定参数)也可以是不带参。

带定参数的装饰器

只能作为参数数量与装饰器数量一致的函数的装饰器。

import time

def deco(func):
    def wrapper(a,b):  #装饰器两个参数
        startTime = time.time()
        func(a,b)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper


@deco
def func1(a,b):  #原函数1两个参数
    print("a+b=")
    time.sleep(1)
    print((a+b))

@deco
def func2(a,b):  #原函数2两个参数
    print("a-b=")
    time.sleep(2)
    print((a-b))

func1(3,5)
a+b=8
time is 1007 ms
func2(8,5)
a-b=3
time is 2016 ms

带有不定参数的装饰器

可以作为不同数量参数函数的装饰器。

import time
def deco(func):
    def wrapper(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper

@deco
def func3(a,b):
    print("a+b=",end="")
    time.sleep(1)
    print((a+b))

@deco
def func4(a,b,c):
    print("a+b+c=",end="")
    time.sleep(1)
    print((a+b+c))


func4(3,4,5)
func3(3,4)
a+b+c=12
time is 1004 ms
a+b=7
time is 1012 ms

 多个装饰器

前面的装饰器在外层,后面的在内层。

import time

def deco01(func):
    def wrapper(*args, **kwargs):
        print("this is deco01")
        startTime = time.time()
        func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
        print("deco01 end here")
    return wrapper

def deco02(func):
    def wrapper(*args, **kwargs):
        print("this is deco02")
        func(*args, **kwargs)
        print("deco02 end here")
    return wrapper

@deco01
@deco02
def func5(a,b):
    print("a*b=",end="")
    time.sleep(1)
    print(a*b)

func5(3,4)
print(func5.__name_)

this is deco01
this is deco02
a*b=12
deco02 end here
time is 1004 ms
deco01 end here
wrapper

functools.wraps 

以上做法原函数被warpper替代了,它重写了函数的名字和注释文档(docstring)。Python提供一个简单的函数来解决这个问题,那就是functools.wraps。

使用functools.wraps

from functools import wraps
 
def a_new_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapTheFunction
 
@a_new_decorator
def func8(a,b):
    print("a*b=",end="")
    time.sleep(1)
    print(a*b)


 
func8(2,5)
print(func8.__name__)
a*b=10
time is 1002 ms
func8

 装饰器类

from functools import wraps
 
class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
 
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile文件,写入
            with open(self.logfile, 'a') as opened_file:
                # 现在将日志输出到指定的文件
                opened_file.write(log_string + '\n')
            # 发送一个通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只输出日志
        pass
@logit()
def myfunc1():
    pass

装饰器使用

授权(Authorization)

装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。

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

日志(Logging)

日志是装饰器运用的另一个亮点。这是个例子:

from functools import wraps
 
def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging
 
@logit
def addition_func(x):
   """Do some math."""
   return x + x
 
 
result = addition_func(4)
# Output: addition_func was called

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ITLiu_JH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值