python装饰器

什么是装饰器?

实例代码1:不传递参数

import time

def decor(fn):
    def wrapper():
        begin_t = time.time()
        fn()
        print("spent time:", time.time() - begin_t)
    return wrapper

@decor
def demo():
    n = 10000
    ret = 0
    for n in range(n):
        ret += n
    print("result:", ret)
    return

print(demo.__name__)
demo()

 

wrapper
result: 49995000
spent time: 0.0019981861114501953

实例代码2:传递一个参数

import time

def decor(fn):
    def wrapper(n):
        begin_t = time.time()
        fn(n)
        print("spent time:", time.time() - begin_t)
    return wrapper

@decor
def demo(n):
    ret = 0
    for n in range(n):
        ret += n
    print("result:", ret)
    return

print(demo.__name__)
demo(100000)
wrapper
result: 49995000
spent time: 0.0019981861114501953

实例代码2:传递两个参数

import time

def decor(fn):
    def wrapper(m, n):
        begin_t = time.time()
        fn(m, n)
        print("spent time:", time.time() - begin_t)
    return wrapper

@decor
def demo(m, n):
    ret = 0
    for i in range(m, n):
        ret += i
    print("result:", ret)
    return

print(demo.__name__)
demo(10, 200000)
wrapper
result: 19999899955
spent time: 0.04311800003051758

实例代码3:装饰器带有参数时

import time
from functools import wraps


def decrator(t):
    print("time:", t.time())
    def decor(fn):
        @wraps(fn)
        def wrapper():
            print('修饰器1:begin')
            fn()
            print('修饰器1:end')
        return wrapper
    return decor


@decrator(t=time)
def demo():
    n = 10000
    ret = 0
    for n in range(n):
        ret += n
    print("result:", ret)
    return


print(demo.__name__)
demo()

被装饰的函数体是如何传入内层的?

装饰器有多个参数,需要以@decrator(t=time)的方式使用,这个时候decrator是已经执行了的(因为加了括号),可以粗略的理解为加载被修饰函数上的是decor,所以这和普通的装饰器并无差别

优化传递的参数

import time
from functools import wraps

def decor(fn):
    @wraps(fn)
    def wrapper(*args):
        begin_t = time.time()
        fn(*args)
        print("spent time:", time.time() - begin_t)
    return wrapper

@decor
def demo(m, n):
    ret = 0
    for i in range(m, n):
        ret += i
    print("result:", ret)
    return

print(demo.__name__)
demo(10, 200000)
  • 函数装饰函数
  • 函数装饰类
  • 类装饰函数
  • 类装饰类

(1)函数装饰函数

(2)函数装饰类

import time
from functools import wraps

def decor(fn):
    @wraps(fn)
    def wrapper(*args):
        ret = fn(*args)
        return ret
    return wrapper

@decor
class demo(object):
    def __init__(self, key, value):
        self.key = key
        self.value = value

    def __str__(self):
        return "<{}={}>".format(self.key, self.value)


print(demo.__name__)
res = demo('name', 'Tom')
print(res)
demo
<name=Tom>

(3)类装饰函数

import time
from functools import wraps

class decor():
    def __init__(self, fn):
        self.__fn = fn

    def __call__(self, *args, **kwargs):
        return self.__fn(*args, **kwargs)


@decor
def demo(m, n):
    ret = 0
    for i in range(m, n):
        ret += i
    print("result:", ret)
    return


print(type(demo))
demo(10, 200000)

(4)类装饰类

import time
from functools import wraps


class decor():
    def __init__(self, cls):
        self.wrpper_class = cls

    def __call__(self, *args, **kwargs):
        if 'key' in kwargs.keys():
            kwargs['key'] = 'wrapper_' + kwargs['key']
        return self.wrpper_class(*args, **kwargs)

@decor
class demo(object):
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value

    def __str__(self):
        return "<{}={}>".format(self.key, self.value)


print(type(demo))
res = demo(key='name', value='Tom')
print(res)

(6)多个修饰函数

import time
from functools import wraps


def decor2(fn):
    @wraps(fn)
    def wrapper2():
        print('修饰器2:begin')
        fn()
        print('修饰器2:end')
    return wrapper2


def decor(fn):
    @wraps(fn)
    def wrapper():
        print('修饰器1:begin')
        fn()
        print('修饰器1:end')
    return wrapper


@decor2
@decor
def demo():
    n = 10000
    ret = 0
    for n in range(n):
        ret += n
    print("result:", ret)
    return


print(demo.__name__)
demo()


demo
修饰器2:begin
修饰器1:begin
result: 49995000
修饰器1:end
修饰器2:end

使用场景

现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。

装饰器能有助于检查某个人是否被授权去使用一个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

使用装饰器的一些坑

1. 错误的函数签名和文档

装饰器装饰过的函数看上去名字没变,其实已经变了,包括doc。使用标准库里的functools.warps基本可以解决这个问题


def decorator(func):
    @warps(func)
    def warpper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

2. 不能用来装饰@classmethod 和 @staticmethod

    因为@staticmethon和@classmethod返回的并不是一个callable对象,而是一个staticmethod的对象,这不符合装饰器的定义。解决方案是把装饰器放在@classmethod和@staticmethod之前。注意这里说的之前不是写在前一行,而是后一行,因为装饰器是从离函数最近的这个开始起作用的

@classmethod
@decorator
def func():
    do_something

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值