概念
所谓decorater,就是wrapper
在函数定义时使用一个包装器,使得这个函数在执行的时候外面码
被裹了一层代码
\color{red}{被裹了一层代码}
被裹了一层代码。
修饰器在被读取时立即执行,因此修饰器函数的定义得在使用之前,编译器来不及找
def 包装器(传入的需要修饰函数):
def 包装器要做什么(*args):
预处理函数()
包装器输出 = 传入的需要修饰函数(*args)
善后函数()
return 包装器输出
return 包装器要做什么
@包装器
def 被包装函数(传入参数a):
return 传出参数
作用
计时器
#定义一个函数用来统计传入函数的运行时间
import time
def timmer(func): #传入的参数是一个函数
def deco(*args, **kwargs): #本应传入运行函数的各种参数
print('\n函数:{_funcname_}开始运行:'.format(_funcname_=func.__name__))
start_time = time.time()#调用代运行的函数,并将各种原本的参数传入
res = func(*args, **kwargs)
end_time = time.time()
print('函数:{_funcname_}运行了 {_time_}秒'
.format(_funcname_=func.__name__, _time_=(end_time - start_time)))
return res#返回值为函数
return deco
初始版本
等价于
t
h
i
s
D
e
c
o
r
a
t
e
d
=
d
e
c
o
r
a
t
e
r
(
t
h
i
s
D
e
c
o
r
a
t
e
d
)
\color {green}{thisDecorated= decorater(thisDecorated) }
thisDecorated=decorater(thisDecorated)
因此装饰器一定得有个输入
还得有个输出
def decorater(func):
print('传入装饰器%s'%func)
return func
@decorater
def thisDecorater():
print("被装饰函数调用")
print('decorater地址%s'%decorater)
print('thisDecorater地址 %s'%thisDecorater)
print("**********************************")
thisDecorater()
print("thisDecorater : ",thisDecorater.__name__)
print(thisDecorater)
print("**********************************")
thisDecorated = decorater(thisDecorater)
thisDecorated()
print(thisDecorated)
print("thisDecorated : ",thisDecorated.__name__)
输出
传入装饰器<function thisDecorater at 0x000001BDF3CB4F70>
decorater地址<function decorater at 0x000001BDF3BE1F70>
thisDecorater地址 <function thisDecorater at 0x000001BDF3CB4F70>**********************************
被装饰函数调用
thisDecorater : thisDecorater<function thisDecorater at 0x000001BDF3CB4F70>**********************************
传入装饰器<function thisDecorater at 0x000001BDF3CB4F70>
被装饰函数调用
<function thisDecorater at 0x000001BDF3CB4F70>
thisDecorated : thisDecorater
奇怪的是,这里的被修饰函数名称和地址竟然还是原来的
被包装函数带传入参数
使用
∗
a
r
g
s
* args
∗args ,这是一个元组truple
注意它只能作为可迭代对象的参数传入迭代器。
关于迭代对象,可以使用以下代码查看。
from collections import abc
isinstance(a,abc.Iterable)
如果没有就得自己实现一下__iter__ 方法。或者,你也可以实现__getitem__方法,在调用iter()迭代器时,编译器也会利用它创建一个__iter__。
被包装函数带传出参数
由于被包装函数原有的return用在了包装器里面,而此时调用返回的return是由包装函数来定的,因此需要 手动把被包装函数返回值给到包装函数return的地方。
def dec(*args):
print(name)
print('pre action')
result = func(*args)
print('post action')
return result
修饰器带传入参数
三层结构
使得修饰器传入参数多样化,便于多态。比如计算时间,传入参数“按秒”、按“GT+8”等方法。
def wap(name):
def decorator1(func):
def dec(*args):
print(name)
print('pre action')
result = func(*args)
print('post action')
return result
return dec
return decorator1
@wap('f1')
def test_f1(name):
print (name)
return None
test_f1('name1')
print(test_f1.__name__)
输出
f1
pre action
被修饰函数 name1
post action
dec
注意这里的函数就被替换了,这才是正常现象
内部逻辑为: test_f1 = wap(‘f1’)(test_f1)
这里wap(‘f1’)返回是decorator1函数对象,这样的话,wap(‘f1’)(test_f1)其实就是decorator1(test_f1),这样就和上面的一样了。只不过这里传递了一个参数’f1’进入decorator内部,使得我们可以操作这个参数。
reference
http://thecodeship.com/patterns/guide-to-python-function-decorators/