1. 无参装饰器
定义:
- 他是一个函数
- 接收一个函数作为它的形参传入
- 返回值也是一个函数
- 可以使用@functionname方式进行简化调用
装饰器和高阶函数:
- 装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
-
使用场景:(一是:记录函数,二是:给函数做一些特殊标记)
def dec(func): def inner(): return func() return inner @dec def f1(): ... return ...
-
例如:将函数调用记录下来
recording = list() def dec(func): def inner(): global recording recording.append(func) return func() return inner @dec def f1(): pass return 'success' @dec def f2(): pass return 'success' print(recording) resp1 = f1() print(recording) resp2 = f2() print(recording)
[] [<function f1 at 0x7fbe30117268>] [<function f1 at 0x7fbe30117268>, <function f2 at 0x7fbe2a40f488>]
-
例如:给函数动态添加属性test
def dec(func): def inner(): # 动态给函数增加属性 try: getattr(func, 'test') except AttributeError as e: setattr(func, 'test', True) print(getattr(func, 'test')) print("被装饰的函数:", func.__name__) return func() return inner @dec def f1(): pass return 'success' resp = f1() print("==============================") print("装饰后返回的函数:", f1.__name__) # 我们发现此时f1并不是原本的f1函数对象了,而是内层函数inner # print(getattr(f1, 'test')) # 所以我们在装饰器内给该函数设置的属性在这里已经获取不到了,这样就背离了我们的初衷,我会在后面讲到如何处理这种情况 print(resp)
True 被装饰的函数: f1 ============================== 装饰后返回的函数: inner success
-
解决上述bug:简单示例
def copy_wrapper(src): def _copy(dst): """用原函数中(f1)的属性覆盖目标函数(inner)相关属性 :param dst: :return: """ dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ dst.__dict__ = src.__dict__ dst.__module__ = src.__module__ ... return dst return _copy def dec(func): """ copy_wrapper(func) ==> 返回 _copy 函数引用; @_copy def inner ==> _copy = _copy(inner) ==> 返回dst(即inner)对象 此时inner对象相关属性已经被替换成被装饰函数的相关属性 """ @copy_wrapper(func) def inner(): ... return func() return inner @dec def f1(): pass return 'success' resp = f1() print(getattr(f1, 'test')) print(resp)
True success
-
对装饰器的改造python内部已经帮我们封装好了
wraps
函数装饰器,我们只需要调用该装饰器就可以直接实现。 -
warps
作用: 消除(被装饰后的函数名等属性的改变)副作用from functools import wraps def dec(func): @wraps(func) def inner(): # 动态给函数增加属性 try: getattr(func, 'test') except AttributeError as e: setattr(func, 'test', True) print(getattr(func, 'test')) print("被装饰的函数:", func.__name__) return func() return inner @dec def f1(): pass return 'success' resp = f1() # 此时的f1对象和原函数f1对象基本上一模一样 print(getattr(f1.__wrapped__, 'test'))
2. 有参装饰器
定义:
- 他是一个函数
- 接收一个函数作为参数传入
- 返回值是一个不带参数的装饰器函数
- 使用@functionname(参数列表)方式调用
- 可以看成是装饰器外层又加了一层函数
-
可以根据装饰器携带参数进行任务分发
import datetime def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))): """记录超时的函数对象,对其进行输出func参数可以根据需求进行数据库存储等操作 params: duration:超时时间 params: func:超时处理函数 return: func """ def _logger(function): # function: 被装饰函数 @wraps(function) def wrapper(*args, **kwargs): # 被装饰函数参数 start_time = datetime.datetime.now() ret = function(*args, **kwargs) delta = (datetime.datetime.now() - start_time).total_seconds() if delta > duration: # 如果调用时间超过duration,则调用func函数进行相关处理 func(function.__name__, duration) return ret return wrapper return _logger
-
例如:flask框架中使用装饰器做路由
def router(self, rule, **options): def decorator(func): # 此处才是真正的内层装饰器函数 def inner(): endpoint = options.pop("endpoint", None) self.add_url_rule(rule, endpoint, func, **options) return func return inner return decorator
3. 被装饰函数带有参数
代码示例:
-
这是最常见的装饰器函数之一
def outer(func): def inner(*args, **kwargs): a, b, c = args if (a > 10) or (b > 10) or (c > 10): return False return func(*args, **kwargs) return inner # 方式一: @outer def my_func(a, b, c): return a + b + c resp = my_func(2, 5, 6) print(resp) # 方式二: func = outer(my_func) # 返回inner函数引用 resp = func(2, 5, 6) # 两步可以精简为一步: resp = outer(my_func)(2, 5, 6) print(resp)
13 13
逻辑解析:
-
绑定装饰器
@outer def my_func(a, b, c): pass resp = my_func(2, 5, 6) print(resp)
-
上述绑定方式主要做了以下事件
outer(myfunc)(a, b, c) """ @outer def my_func 相当于 myfunc = outer(myfunc) """ """ 此处调用的my_func已经不再是我们当初的函数对象my_func,而是经过装饰器装饰过的对象 resp = my_func(2, 5, 6) """
-
执行逻辑
第一步:
outer(myfunc)
--> 返回sub_func
函数的引用第二步:
sub_func(a, b, c)
执行sub_func
函数第三步:执行if判断
第四步:
return func(a, b, c)
执行func
函数调用结果,并返回
4.带参类装饰期
In [63]: class Test:
...: def check(self, *args, **kwargs):
...: print(self)
...: print("来自装饰器自身需要的参数:", *args)
...: def outer(func):
...: def inner(num):
...: print("开始调用内部方法get_test")
...: self.get_test(num)
...: print("内部方法get_test调用结束")
...: return func(num)
...: return inner
...: return outer
...:
...: def get_test(self, num):
...: print("接收到外部参数:%d" % num)
...:
...:
In [64]:
In [64]: test = Test()
In [65]: @test.check("------001------")
...: def test(args):
...: resp = args + 1
...: print("得到结果:", resp)
...: return resp
...:
...:
<__main__.Test object at 0x00000229DDF61E80>
来自装饰器自身需要的参数: ------001------
In [66]: test(123)
开始调用内部方法get_test
接收到外部参数:123
内部方法get_test调用结束
得到结果: 124
Out[66]: 124