python-装饰器

定义

在不改变原函数的调用方式的情况下,在函数前后添加功能

一、完美的装饰器

"""
完美的装饰器
"""

import functools
import time


def timer(func):
    @functools.wraps(func)
    def wrap(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs)
        end_time = time.time()
        time_interval = end_time - start_time
        print('【%s】 函数 调用了 %s 秒' % (func.__name__, time_interval))
        return ret

    return wrap


# 装饰器等价于:hello_world = timer(hello_world)
@timer
def hello_world():
    time.sleep(0.2)
    print('hello world')
    return 'ok'


print(hello_world())
# print(hello_world.__name__)  # @functools.wraps(func)的作用:使hello_world.__name__正确显示

二、带参数的装饰器

"""
需求:要求装饰器装饰函数的时候,可以传一个值,这个值表示 timer 这个装饰器是否启用:(True 启用)(False 不启用)
使用过的场景:functools.wraps
"""

import time
from functools import wraps


def timer(flag):
    def inner_timer(func):
        @wraps(func)
        def wrap(*args, **kwargs):
            if flag:
                start_time = time.time()
                ret = func(*args, **kwargs)
                end_time = time.time()
                print('%s 程序运行了 %s 秒' % (func.__name__, end_time - start_time))
                return ret
            else:
                return func(*args, **kwargs)

        return wrap

    return inner_timer


# 装饰器等价于:hello_world = timer(True)(hello_world)
@timer(True)
def hello_world():
    time.sleep(0.2)
    return 'ok'


print(hello_world())
# print(hello_world.__name__)

三、多个装饰器装饰一个函数

"""
多个装饰器装饰一个函数,谁离被装饰的函数近,谁就先装饰。
"""

import time
from functools import wraps


def wrap1(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print('wrap1 装饰前')
        ret = func(*args, **kwargs)
        print('wrap1 装饰后')
        return ret

    return inner


def wrap2(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print('wrap2 装饰前')
        ret = func(*args, **kwargs)
        print('wrap2 装饰后')
        return ret

    return inner


@wrap1
@wrap2
def hello_world():
    time.sleep(0.3)
    print('hello world')


hello_world()
"""
输出:
wrap1 装饰前
wrap2 装饰前
hello world
wrap2 装饰后
wrap1 装饰后
"""

补充:完美的装饰器

经过装饰器装饰之后的函数,他们的__name__已经从原来的'now'变成了'inner'

 1 def wrapper(func):
 2     def inner(*args, **kwargs):
 3         print("在调用被装饰的函数前执行的代码")
 4         ret = func(*args, **kwargs)
 5         print("在调用被装饰的函数前执行的代码")
 6         return ret
 7     return inner
 8 
 9 
10 @wrapper
11 def now():
12     print('2018-8-19')
13 
14 
15 print(now.__name__)    # inner
View Code

因为返回的那个inner()函数名字就是'inner',所以,需要把原始函数的__name__等属性复制到inner()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

 1 import functools
 2 
 3 
 4 def wrapper(func):
 5     @functools.wraps(func)
 6     def inner(*args, **kwargs):
 7         print("在调用被装饰的函数前执行的代码")
 8         ret = func(*args, **kwargs)
 9         print("在调用被装饰的函数前执行的代码")
10         return ret
11     return inner
12 
13 
14 @wrapper
15 def now():
16     print('2018-8-19')
17 
18 
19 print(now.__name__)     # now
View Code

 

posted @ 2018-08-19 23:28  孙昌恒  阅读(212)  评论(0编辑  收藏  举报