python 装饰器

装饰器(decorator):它能对任何可调用的对象进行包装,既能够用于方法也能够用于函数,想要对一个已有的模块做一些”修饰工作”,但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去,

函数名是某个函数的引用(reference),所以,我们可以对同一个函数设置不同的函数名.(可以理解为,函数也是对象,可以通过赋值来设置不同的对象名)

1
2
3
4
5
6
7
8
9
10
11
12
>>> def succ(x):
return x+1
>>> successor = succ
>>> successor(10) # 11
>>> succ(10) # 11
# successor和succ都指向同一个函数(应该是对象),都指向同一个内存区域.
>>> id(successor) # 139701625513368
>>> id(succ) # 139701625513368
>>> del succ
>>> successor(10) # 11
>>> type(successor) # function

写代码要遵循开放封闭原则

虽然在这个原则是⽤的⾯向对象开发,但是也适⽤于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
封闭:已实现的功能代码块 开放:对扩展开发

装饰器什么时候进行装饰

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')
# 其解释器将此方法解释为
# foo = decorator(foo)
# 把一个函数当参数传到另一个函数中,然后再回调
if __name__ == '__main__':
foo
# hello
foo()
# hello
# foo end
foo_text = decorator(foo)
print(type(foo_text))
# hello
# hello
# <class 'function'>
foo_text()
# foo end

装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的(被装饰)的函数

下面这个例子向我们展示了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")
# foo = hello(foo)
if __name__ == '__main__':
foo()
# hello, foo
# i am foo
# goodby, foo

类似地

1
2
3
4
5
6
7
8
9
10
11
12
13
# 多个decorator
@decorator_one
@decorator_two
def func():
pass
# func = decorator_one(decorator_two(func))
# 带参数的decorator
@decorator(arg1, arg2)
def func():
pass
# func = decorator(arg1,arg2)(func)
# 这意味着decorator(arg1, arg2)这个函数需要返回一个"真正的decorator"

无参数装饰器-包装带参数函数

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)
# handle args start
# 1 2
# handle args end
# 先传递函数名,再传递参数
foo2_text = decorator_func_args(foo2)
print('--')
foo2_text(1)
# --
# handle args start
# handle args start
# 1 2
# handle args end
# handle args end

带参数装饰器 – 包装无参数函数

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()
# deco_args
# <function foo3 at 0x7fba7bfcf7b8>
# 先传递参数,再传递函数名
foo3_text = decorator_with_params("deco_args")
print('--')
foo3 = foo3_text(foo3)
# deco_args
# <function foo3 at 0x7fba7bfcf7b8>
# deco_args
# --
# <function foo3 at 0x7fba7bfcf7b8>
func = decorator_with_params('1')(foo3)
# deco_args
# <function foo3 at 0x7ff913a6f730>
# 1
# <function foo3 at 0x7ff913a6f730>

带参数装饰器– 包装带参数函数

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)
# begin
# content
# end
# 123 <function foo4 at 0x7f4f0f056730> (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)
# **
# @@
# begin
# begin
# content
# end
# 123 <function foo4 at 0x7f663fd82730> (1,) {'b': 3}
# end
# 123 <function decorator_with_params_and_func_args.<locals>.handle_func.
# <locals>.handle_args at 0x7f663fd827b8> (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'))
# <b class='bold_css'><i class='italic_css'>hello world femn</i></b>

makeHtmlTag有两个参数.所以,为了让 hello = makeHtmlTag(arg1, arg2)(hello) 成功,
makeHtmlTag 必需返回一个decorator(这就是为什么我们在makeHtmlTag中加入了real_decorator()的原因),
这样一来,我们就可以进入到 decorator 的逻辑中去了——
decorator得返回一个wrapper,wrapper里回调hello. “

函数的元数据

函数的一些属性:

  1. name(函数名),
  2. doc(描述)和
  3. 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__()")
# 在我们给某个函数decorator时被调用,所以
# 需要有一个fn的参数,也就是被decorator的函数
self.fn = fn
# 调用被decorator函数时被调用的
# 也可以在这个传入fn __call__(self.fn)
def __call__(self):
self.fn()
print( "inside myDecorator.__call__()")
@myDecorator
def aFunction():
print( "inside aFunction()")
print( "Finished decorating aFunction()")
if __name__ == '__main__':
aFunction()
# inside myDecorator.__init__()
# Finished decorating aFunction()
# inside aFunction()
# inside myDecorator.__call__()

下面这个示例展示了通过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 = {}
# decorator类中没有__call__(),但是wrapper返回了原函数
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'))
# This is the main page.
# This is the next page.

Share Comments