Python装饰器

1.什么是装饰器

  • 装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数;

  • 装饰器的功能在于对函数或类功能的增强,这是一种低耦合的功能增强;

2.装饰器特点

  • 开放封闭原则,即对扩展是开放的,对修改时封闭的;

  • 装饰器本质可以是任意可调用的对象,被装饰的对象也可以是任意可调用对象;

  • 装饰器的功能是在不修改被装饰器对象源代码以及被装饰器对象的调用方式的前提下为其扩展新功能;

  • 装饰器本质是函数,(即装饰其他函数)就是为其他函数添加附加功能;

3.无参装饰器


# 例1:这就是装饰器的原型,目的是对add函数增强,在add执行的前后可以做一些操作。
def add(x, y):
    return x + y
def decorator(fn):
    def wrapper(*args, **kwargs):
        print('start')
        result = fn(*args, **kwargs)
        print('end')
        return result
    return wrapper
wrapper = decorator(add)
result = wrapper(x=1, y=2)
print(result)
# 例2:可以将例1的调用过程进行改造
def add(x, y):
    return x + y
def decorator(fn):
    def wrapper(*args, **kwargs):
        print('start')
        result = fn(*args, **kwargs)
        print('end')
        return result
    return wrapper
result = decorator(add)(x=1, y=2)
print(result)
# 例3:decorator 就是装饰器函数,add就是被装饰的函数
def decorator(fn):
    def wrapper(*args, **kwargs):
        print('start')
        result = fn(*args, **kwargs)
        print('end')
        return result
    return wrapper
#这是Python的装饰器语法糖,@decorator相当于对add函数进行了一次调用,返回的是wrapper
@decorator
def add(x, y):
    return x + y
result = add(x=1, y=2)
print(result)
# 例4:解决被装饰器装饰的函数属性不一致的问题
import functools
def decorator(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        print('start')
        result = fn(*args, **kwargs)
        print('end')
        return result
    return wrapper
@decorator
def add(x, y):
    return x + y
result = add(x=1, y=2)
print(result)
print(add.__name__)  # add

  • 就是不带参数的装饰器,是最简单的装饰器,返回包裹函数;

  • *args表示的参数以列表的形式传入;

  • **kwargs表示的参数以字典的形式传入;

  • @decorator 这个语法相当于 执行 func = decorator(func),为func函数装饰并返回,且@修饰符必须出现在函数定义前一行,不允许和函数定义在同 一行;

4.带参装饰器


import functools
import datetime
import time
def decorator(t): # 这里只是一个简单的函数
    def timer(fn): # 这里才是装饰器
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            '''
            this is a wrap function
            '''
            print("args={} kwargs={}".format(args,kwargs))
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            end = datetime.datetime.now()
            duration = (end - start).total_seconds()
            if duration > t:
                print('Time out')
            return ret
        return wrapper
    return timer
@decorator(2)
def add(x,y):
    '''
    :param x:
    :param y:
    :return: 返回x + y的结果
    '''
    time.sleep(3)  # 延迟3秒
    return x + y
add(1,2)
print(add.__name__, add.__doc__, sep='\n')

  • 就是带参数的装饰器,即复杂装饰器,返回包裹函数;

  • 实现一个装饰器,它用来检查被装饰函数的参数类型,装饰器可以通过参数指明函数参数的类型,调用时如果检测出类型不匹配则抛出异常。

5.functools模块


@functools.wraps(fn)

  • 用来拷贝属性;

  • functools,用于高阶函数,指那些作用于函数或者返回其它函数的函数,通常只要是可以被当做函数调用的对象就是这个模块的目标。

6.装饰器在不同场景下的使用

(1)带参装饰器:不返回包裹函数


# 这里装饰器返回的并不是包裹的wrapper函数,因为这里没有wrapper函数,而是被装饰的函数本身
# 这里装饰器的意义在于:调用被装饰函数之前可以做一些事情
import datetime
import time
def decorator(t):
    def timer(fn):
        start = datetime.datetime.now()
        duration = (datetime.datetime.now() - start).total_seconds()
        print(duration)  # 0.0
        print(t)  # 2
        return fn
    return timer
@decorator(2)
def add(x,y):
    time.sleep(2)
    return x+y
print(add(1,2))
print(add.__name__)

  • 装饰器返回的并不是包裹函数,而是直接返回原始函数,这种情况只能在调用原始函数之前做一些功能增强;

  • 如果返回包裹函数,不仅在调用函数之前做功能增强,调用函数之后也能执行功能增强;

(2)不带参装饰器:不返回包裹函数,返回原始函数本身


# 不带参数的装饰器,直接返回被装饰函数本身,在原始函数之前做一些事情
import time
def decorator(fn):
    print('调用函数之前我可以做些事情')
    return fn
@decorator
def add(x,y):
    time.sleep(2)
    print(x+y)  # 3
add(1,2)

  • 装饰器同样是直接返回被装饰函数本身,在原始函数之前做一些事情;

  • 函数装饰器装饰类:返回原始类本身(不带参数)

  • 在调用一个类之前给这个类封装一些属性和方法,然后调用这个类时就可以直接使用这些方法和属性;

(3)函数装饰器装饰类:返回原始类本身(带参数)


def dec(cls):
    cls.num = 100
    return cls
@dec
class Person(): # Person = cls
    def __init__(self):
        pass
print(Person.num) # 100

  • 在带参装饰器第一次调用的时候只是一个简单函数调用,返回的那个函数才是正真的装饰器,装饰器装饰了作为参数传递给它的那个类,然后这个参数绑定了一个属性,调用这个参数的类变量,就是第一次简单函数调用所带的参数;

(4)函数装饰器装饰类:返回原始类本身(带参数)


def dec(num):
    def wrapper(cls): # 这里的cls就是Person类
        cls.num = num
        return cls
    return wrapper
# 这种带参数的装饰器就理解为函数调用即可
@dec(100)
class Person():
    def __init__(self):
        pass
print(Person.num) # 100

7.类装饰器


import datetime
import time
from functools import wraps
# 这是上下文管理的类
class Decorator:
    '''this is dec class '''
    def __init__(self, fn):
        if fn is not None:
            self.fn = fn
            wraps(fn)(self)  # 这里是将fn的属性赋值给self实例,因为self实例会返回
    def __call__(self, *args, **kwargs):
        self.start = datetime.datetime.now()
        ret = self.fn(*args, **kwargs)
        self.interval = (datetime.datetime.now() - self.start).total_seconds()
        return 'interval is {}, result is {}'.format(self.interval, ret)
# 被装饰的函数
@Decorator    # add = Decorator(add)
def add(x,y):
    ''' this is a function '''
    time.sleep(2) # 延迟2秒
    return x + y
print(add(3,4))  # add调用的是Decorator的实例self,不是以前的函数add了
print(add.__doc__)

  • 类装饰器顾名思义用类写的装饰器,类可以装饰类,也可以装饰函数;

  • 使用类装饰器可以通过继承的方式扩展类装饰器的行为;

参考:侠课岛( 9xkd.com )Python同学计划

    原文作者:Winter
    原文地址: http://blog.itpub.net/69908432/viewspace-2639506/
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞