python 装饰器

装饰器的作用

在不改变原函数(对象)本身和原函数(对象)的调用方式的情况下, 为其添加额外的功能. 再简单点说, 装饰器的作用就是为已经存在的函数或对象添加额外的功能.


装饰器的使用

被装饰函数的上方写 @decorator , 这里的符号 @ 是必须的, decorator是你定义的装饰器的名字.


装饰器(版本一)

描述: 最简单的装饰器, 原函数既没有形参也没有返回值.

def wrapper1(func):
    def inner():
        print("在原函数执行前要额外添加的功能")
        func()
        print("在原函数执行后要额外添加的功能")

    return inner

实例说明:

def wrapper1(func):		# 自定义一个装饰器wrapper1
    def inner():
        print("在原函数执行前要额外添加的功能")
        func()
        print("在原函数执行后要额外添加的功能")
        
    return inner


@wrapper1	# 使用装饰器
def func():
    print("我是原函数func")

    
func()	# 调用原函数


### 执行结果:
在原函数执行前要额外添加的功能
我是原函数func
在原函数执行后要额外添加的功能

装饰器(版本二)

描述: 原函数有返回值, 但没有任何形参.

def wrapper2(func):
    def inner():
        print("在原函数执行前要额外添加的功能")
        ret = func()  	# 在这里ret接收func的返回值
        print("在原函数执行后要额外添加的功能")
        return ret  	# inner函数会返回"原函数的返回值"

    return inner

实例说明:

def wrapper2(func):
    def inner():
        print("在原函数执行前要额外添加的功能")
        ret = func()  	# 在这里ret接收func的返回值
        print("在原函数执行后要额外添加的功能")
        return ret  	# inner函数会返回"原函数的返回值"

    return inner


@wrapper2
def func2():
    print("我是原函数func2")
    return "我是原函数func2的返回值"


ret = func2()
print(ret)


### 执行结果:
在原函数执行前要额外添加的功能
我是原函数func2
在原函数执行后要额外添加的功能
我是原函数func2的返回值

装饰器(版本三)

描述: 原函数不仅有返回值, 而且还有参数

def wrapper3(func):
    def inner(*args, **kwargs):  	 # inner函数接收所有参数
        print("在原函数执行前要额外添加的功能")
        ret = func(*args, **kwargs)  # func函数拿到所有参数
        print("在原函数执行后要额外添加的功能")
        return ret

    return inner

实例说明:

def wrapper3(func):
    def inner(*args, **kwargs):  # inner函数接收所有参数
        print("在原函数执行前要额外添加的功能")
        ret = func(*args, **kwargs)  # func函数拿到所有参数
        print("在原函数执行后要额外添加的功能")
        return ret

    return inner


@wrapper3
def func3(name):
    print("我是原函数<{}>".format(name))
    return "我是原函数<{}>的返回值".format(name)


ret = func3(name="function3")	# 设置函数func3的形参name="function3"
print(ret)


### 执行结果:
在原函数执行前要额外添加的功能
我是原函数<function3>
在原函数执行后要额外添加的功能
我是原函数<function3>的返回值

装饰器(进阶版)

描述: 如果我们存在这样的需求----当我们给所有需要添加额外功能的函数加上装饰器以后, 某一天, 产品经理突然告诉我们, 这些地方需要这些额外功能, 另外一些地方却又不需要这些额外功能了, 此时我们该怎么办呢? 没关系, 我们还有另外一种简单便捷的方法来解决这个问题. 即: 带参数的装饰器. 有了参数的装饰器可以让我们更加随心所欲地给函数添加或关闭额外功能.

def outer1(flag=True):  	# 参数flag默认为True
    def wrapper(func):
        def inner(*args, **kwargs):
            if flag:  		# 当flag为'真'时, 给原函数添加额外功能
                print("在原函数执行前要额外添加的功能")
                ret = func(*args, **kwargs)
                print("在原函数执行后要额外添加的功能")
            else:  			# 当flag为'假'时, 对原函数不进行任何操作
                ret = func(*args, **kwargs)
            return ret

        return inner

    return wrapper

实例说明:

def outer2(flag=True):
    def wrapper(func):
        def before():
            print('我是函数before,我在原函数之前执行')

        def after():
            print('我是函数after,我在原函数之后执行')

        def inner(*args, **kwargs):
            if flag:
                before()
                ret = func(*args, **kwargs)
                after()
            else:
                ret = func(*args, **kwargs)
            return ret

        return inner

    return wrapper


@outer2()
def func(name):
    print("我是原函数<{}>".format(name))
    return "我是原函数<{}>的返回值".format(name)


ret = func("function1")
print(ret)


### 执行结果:
我是函数before,我在原函数之前执行
我是原函数<function1>
我是函数after,我在原函数之后执行
我是原函数<function1>的返回值

需要注意的是, 以上代码只是给装饰器加装饰器的其中一种做法, 事实上, 你是可以根据你的产品需求, 来任意改变装饰器内部的业务逻辑的.


多个装饰器装饰同一个函数

实例说明:

from functools import wraps  # 引入wraps, 使用装饰器修复技术


def wrapper1(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("wrapper1--前")
        ret = func()
        print("wrapper1--后")
        return ret

    return inner


def wrapper2(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("wrapper2--前")
        ret = func()
        print("wrapper2--后")
        return ret

    return inner


# 多个装饰器同时装饰同一个函数
@wrapper1
@wrapper2
def func():
    print("这里是函数func")


func()

# 执行结果:
wrapper1--前
wrapper2--前
这里是函数func
wrapper2--后
wrapper1--后


装饰器修复技术

描述: 当我们使用了装饰器之后, 我们发现, 当我们尝试执行print(func2.__name__)print(func2.__doc__)时, 打印出来的结果却是装饰器内部的函数名, 我们无法正常获取到自己定义的函数的名字和说明文档了. 此时我们需要使用"装饰器修复技术".

实例说明: 未使用修复技术

# 自定义装饰器wrapper
def wrapper(func):
    def inner(*args, **kwargs):
        print("原函数之前要执行的操作")
        ret = func(*args, **kwargs)
        print("原函数之后执行的操作")
        return ret

    return inner


@wrapper  # 加装饰器
def func(name):  # 自定义一个函数func
    """
    这里是函数func的文档说明
    :param name:
    :return:
    """
    print("我是原函数<{}>".format(name))
    return "我是原函数<{}>的返回值".format(name)


print(func.__name__)
print(func.__doc__)

### 执行结果:
inner
none

实例说明: 使用装饰器修复技术

描述: 要使用装饰器修复技术, 我们必须from functools import wraps. 然后, 在装饰器内部书写@wraps(func).

from functools import wraps	# 使用装饰器修复技术必须要引入wraps

# 自定义装饰器wrapper2
def wrapper2(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("原函数之前要执行的操作")
        ret = func(*args, **kwargs)
        print("原函数之后执行的操作")
        return ret

    return inner


@wrapper2  # 加装饰器
def func2(name):  # 自定义一个函数func
    """
    这里是函数func2的文档说明
    :param name:
    :return:
    """
    print("我是原函数<{}>".format(name))
    return "我是原函数<{}>的返回值".format(name)

print(func2.__name__)
print(func2.__doc__)

### 执行结果:
func2

    这里是函数func2的文档说明
    :param name:
    :return:

对比以上代码可以发现, 使用了装饰器修复技术后, 我们可以正常执行print(func2.__name__)print(func2.__doc__), 从而拿到我们想要的结果了.


Django中的CBV模式下加装饰器的正确方式

给CBV加装饰器

  • a. 首先要引入模块.
from django.utils.decorators import method_decorator
  • b. 自定义装饰器timer: 用于得到执行该函数所消耗的时间.
import time
def timer(func):
    def inner(*args, **kwargs):
        start = time.time()		# 函数执行起始时间
        ret = func(*args, **kwargs)
        end = time.time()		# 函数执行完毕时间
        print("执行该函数所消耗的时间为{}".format(end-start))
        return ret
    return inner
  • c. 加在某个get/post方法上:
@method_decorator(timer)
def get(self, request):
    """你的业务逻辑"""
  • d. 加在self.dispatch方法上:
@method_decorator(timer)
def dispatch(self, request, *args, **kwargs):
    """业务逻辑"""
    ret = super().dispatch(request, *args, **kwargs)
    """业务逻辑"""
    return ret
  • e. 加在某个类上, 以AddPublisher为例:
@method_decorator(timer, name='post')
@method_decorator(timer, name='get')
class AddPublisher(View):
    def get(self, request):
        pass
    def post(self, request):
        pass
posted @ 2018-12-04 21:06  咕噜噜~  阅读(260)  评论(0编辑  收藏  举报