装饰器(decorator):它能对任何可调用的对象进行包装,既能够用于方法也能够用于函数,想要对一个已有的模块做一些”修饰工作”,但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去,
函数名是某个函数的引用(reference),所以,我们可以对同一个函数设置不同的函数名.(可以理解为,函数也是对象,可以通过赋值来设置不同的对象名)
1 2 3 4 5 6 7 8 9 10 11 12
| >>> def succ(x): return x+1 >>> successor = succ >>> successor(10) >>> succ(10) >>> id(successor) >>> id(succ) >>> del succ >>> successor(10) >>> type(successor)
|
写代码要遵循开放封闭原则
虽然在这个原则是⽤的⾯向对象开发,但是也适⽤于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
封闭:已实现的功能代码块 开放:对扩展开发
装饰器什么时候进行装饰
python解释器对函数默认是不执行的,只是知道这块有这些个函数,在调用的时候才执行
@der # 但只要是python解释器执行到这个代码了,那么就会自动的进行装饰,而不是等到调用的时候才装饰的

结果如下
1 2 3 4 5
| ---正在装饰2---- ---正在装饰1---- ---正在验证权限1---- ---正在验证权限2---- ---f1---
|
使用装饰器对有返回值的函数进行装饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def func(functionName): print("---func---1---") def func_in(): print("---func_in---1---") ret = functionName() print("---func_in---2---") return ret print("---func---2---") return func_in @func def test(): print("----test----") return "haha" ret = test() print("test return value is %s"%ret)
|
装饰器(decorator)功能
1.引⼊⽇志 2.函数执⾏时间统计 3.执⾏函数前预备处理 4.执⾏函数后清理功能 5.权限校验等场景 6.缓存
装饰器的两种类型:函数装饰器和类装饰器
函数装饰器
无参数装饰器-包装无参数函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| def decorator(func): print('hello') return func @decorator def foo(): print('foo end') if __name__ == '__main__': foo foo() foo_text = decorator(foo) print(type(foo_text)) foo_text()
|
装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的(被装饰)的函数
下面这个例子向我们展示了decorator的本质:hello(foo)返回了wrapper()函数,所以,foo其实变成了wrapper的一个变量,而后面的foo()执行其实变成了wrapper()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def hello(fn): def wrapper(): print("hello, %s" % fn.__name__) fn() print("goodby, %s" % fn.__name__) return wrapper @hello def foo(): print("i am foo") if __name__ == '__main__': foo()
|
类似地
1 2 3 4 5 6 7 8 9 10 11 12 13
| @decorator_one @decorator_two def func(): pass @decorator(arg1, arg2) def func(): pass
|
无参数装饰器-包装带参数函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| def decorator_func_args(func): def handle_args(*args, **kwargs): print("handle args start") func(*args, **kwargs) print("handle args end") return handle_args @decorator_func_args def foo2(a, b=2): print(a,b) if __name__ == '__main__': foo2(1) foo2_text = decorator_func_args(foo2) print('--') foo2_text(1)
|
带参数装饰器 – 包装无参数函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| def decorator_with_params(arg_of_decorator): print(arg_of_decorator) def newDecorator(func): print(func) return func return newDecorator @decorator_with_params("deco_args") def foo3(): pass if __name__ == '__main__': foo3() foo3_text = decorator_with_params("deco_args") print('--') foo3 = foo3_text(foo3) func = decorator_with_params('1')(foo3)
|
带参数装饰器– 包装带参数函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| def decorator_with_params_and_func_args(arg_of_decorator): def handle_func(func): def handle_args(*args, **kwargs): print("begin") func(*args, **kwargs) print("end") print(arg_of_decorator, func, args, kwargs) return handle_args return handle_func @decorator_with_params_and_func_args("123") def foo4(a, b=2): print("content") if __name__ == '__main__': foo4(1, b=3) foo4_dec = decorator_with_params_and_func_args("123") print('**') foo4_func = foo4_dec(foo4) print('@@') foo4_text = foo4_func(1, b=3)
|
给decorator参数赋值,返回handle_func对象
handle_func()函数接收foo4对象,返回handle_args对象
handle_args()函数接收传递给foo4的参数.
一个有点意义的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| def makeHtmlTag(tag, *args, **kwds): def real_decorator(fn): css_class = " class='{0}'".format(kwds["css_class"]) \ if "css_class" in kwds else "" def wrapped(*args, **kwds): return "<" + tag + css_class + ">" + fn(*args,**kwds) + "</" + tag + ">" return wrapped return real_decorator @makeHtmlTag(tag="b", css_class="bold_css") @makeHtmlTag(tag="i", css_class="italic_css") def hello(name): return "hello world {0}".format(name) if __name__ == '__main__': print(hello('femn'))
|
makeHtmlTag有两个参数.所以,为了让 hello = makeHtmlTag(arg1, arg2)(hello) 成功,
makeHtmlTag 必需返回一个decorator(这就是为什么我们在makeHtmlTag中加入了real_decorator()的原因),
这样一来,我们就可以进入到 decorator 的逻辑中去了——
decorator得返回一个wrapper,wrapper里回调hello. “
函数的元数据
函数的一些属性:
- name(函数名),
- doc(描述)和
- module(模块位置)
在使用装饰器的时候,函数会丢失这些元数据.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def greeting(func): def function_wrapper(x): """function_wrapper of greeting""" print("Hi, " + func.__name__ + " returns:") return func(x) return function_wrapper @greeting def f(x): """just some silly function""" return x + 4 if __name__ == '__main__': f(10) print("function name: " + f.__name__) print("docstring: " + f.__doc__) print("module name:" + f.__module__) ")
|
运行结果如下
1 2 3 4
| Hi, f returns: function name: function_wrapper docstring: function_wrapper of greeting module name:__main__
|
在正常情况下,f.name应该为f,而通过装饰器之后,由于没有保存函数的元数据,所以变成了function_wrapper.
每当定义一个装饰器时,应该总是记得为底层的包装函数添加functools库中的@wraps装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from functools import wraps def greeting(func): @wraps(func) def function_wrapper(x): """function_wrapper of greeting""" print("Hi, " + func.__name__ + " returns:") return func(x) return function_wrapper @greeting def f(x): """just some silly function""" return x + 4 if __name__ == '__main__': f(10) print("function name: " + f.__name__) print("docstring: " + f.__doc__) print("module name:" + f.__module__) ")
|
运行结果如下
1 2 3 4
| Hi, f returns: function name: f docstring: just some silly function module name:__main__
|
在python中圆括号意味着调用函数.在没有圆括号的情况下,python会把函数当作普通对象
而想引用对象时则必须要使用圆括号,而且必须要重写call方法,才能调用
1 2 3 4 5 6 7 8 9 10 11
| class Test(): def __call__(self, *args, **kwargs): print('类对象的直接调用时,必须要重写的方法') def fun(): print('方法的引用之后的直接调用') t = Test() t() f = fun f()
|
类装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class myDecorator(object): def __init__(self, fn): print( "inside myDecorator.__init__()") self.fn = fn def __call__(self): self.fn() print( "inside myDecorator.__call__()") @myDecorator def aFunction(): print( "inside aFunction()") print( "Finished decorating aFunction()") if __name__ == '__main__': aFunction()
|
下面这个示例展示了通过URL的路由来调用相关注册的函数示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class MyApp(): def __init__(self): self.func_map = {} def register(self, name): def func_wrapper(func): self.func_map[name] = func return func return func_wrapper def call_method(self, name=None): func = self.func_map.get(name, None) if func is None: raise Exception("No function registered against - " + str(name)) return func() app = MyApp() @app.register('/') def main_page_func(): return "This is the main page." @app.register('/next_page') def next_page_func(): return "This is the next page." print(app.call_method('/')) print(app.call_method('/next_page'))
|