装饰器是可调用的对象,其参数是另外一个函数(被装饰的函数)。 python的世界中,一切皆对象,把函数看作一个对象,一个能够拼接、编辑的对象。执行对象的方法是__ 对象()__python
观察下面的两个调用,他们的效果是同样的。flask
#!/usr/bin/env python
#-*-coding:utf-8-*-
''' @file: dec4.py @time: 2018/12/13 17:15 '''
import functools
def register(func):
@functools.wraps(func)
def warpper(*args,**kwargs):
print("啦啦啦,这里装饰函数 %s"%func.__name__)
result = func() #这里就是真正的执行逻辑
return result
return warpper # 返回包装后的函数
@register
def f1():
print("使用@语法的装饰器 %s"%f1.__name__)
def f2():
print("没@语法的装饰器 %s" % f2.__name__)
if __name__ == '__main__':
f1()
register(f2)()
#或者
# f_ = register(f2)
# f_()
复制代码
装饰器的一个关键特性是,它们在被装饰的函数定义以后当即运行。 这一般是在导入时(即 Python 加载模块时)。--cookbookapp
建立文件 dec1.py框架
# dec1.py
registry = [] # 存放注册函数
def register(func):
print('装饰器入列表(%s)' % func.__name__)
registry.append(func)
return func
@register
def f1():
print('执行 %s'%f1.__name__)
@register
def f2():
print('执行 %s' % f2.__name__)
复制代码
建立文件 dec2.py函数
# dec2.py
from 你的目录 import dec1
复制代码
执行文件 dec2.py 输出以下:spa
一、返回目标函数。 二、返回目标函数的结果。日志
以上 register的用法适用于对函数进行预处理的场景,例如flask框架的路由注册,在flask app启动时将全部的函数收集起来,与注册的路径进行一一对应,可是函数运行时跟原函数没有任何区别,由于装饰器返回的仍是目标函数。假如咱们须要在函数运行时进行日志的输出,则须要将函数进行一层封装,即将目标函数包装成另外一个函数,这时返回的是另外一个函数,只是包装后的函数能够进行额外的操做。code
如下是两个装饰器的写法,注意区别。cdn
import functools
def register1(func): #在文件引入时返回目标函数的装饰器
#这里进行一些函数预处理的操做,例如将函数名字收集进一个列表
#list.append(func.__name__)
return func
def register2(func): #在函数运行时返回函数的装饰器
@functools.wraps(func)
def warpper(*args,**kwargs):
print("函数没有执行 %s"%func.__name__)
result = func #这里就是真正的执行逻辑 #注意-----这里没有执行函数
return result
return warpper # 返回包装后的函数
def register3(func): #返回目标函数结果的装饰器
@functools.wraps(func)
def warpper(*args,**kwargs):
print("函数开始执行 %s"%func.__name__)
result = func() #这里就是真正的执行逻辑
return result
return warpper # 返回包装后的函数
@register1
def f1():
print("执行函数 %s"%f1.__name__)
@register2
def f2():
print("执行函数 %s"%f2.__name__)
@register3
def f3():
print("执行函数 %s"%f3.__name__)
if __name__ == '__main__':
f1()
f2()
f3()
复制代码
执行结果:对象
这时添加代码:
if __name__ == '__main__':
f1()
f2()
f_ = f2()
print("开始真正执行f2函数啦")
f_()
f3()
复制代码
执行结果:
你会发现下面的代码中,若是wrapper函数没有使用@functools.wraps装饰的话,f1.__name__返回的是wrapper函数的名字,这是由于咱们返回的函数已是wrapper这个函数,因此__name__属性也就不是原来函数的了,@functools.wraps装饰器的做用就是将目标函数的属性例如__name__等原封不动转移给包装好的函数。
#!/usr/bin/env python
#-*-coding:utf-8-*-
''' @file: dec4.py @time: 2018/12/13 17:15 '''
import functools
def register(func): #返回目标函数结果的装饰器
def warpper(*args,**kwargs):
result = func() #这里就是真正的执行逻辑
return result
return warpper # 返回包装后的函数
def register2(func): #返回目标函数结果的装饰器
@functools.wraps(func)
def warpper(*args,**kwargs):
result = func() #这里就是真正的执行逻辑
return result
return warpper # 返回包装后的函数
@register
def f1():
print("函数名为 %s"%f1.__name__)
@register2
def f2():
print("函数名为 %s" % f2.__name__)
if __name__ == '__main__':
f1()
f2()
#或者
#f_ = register(f2)
#f_()
复制代码
执行结果:
装饰器也是一个对象,只要使用一个函数封装装饰器,返回目标装饰器便可,这时就能够经过外层的函数传进参数。
#!/usr/bin/env python
# -*-coding:utf-8-*-
''' @file: dec5.py @time: 2018/12/13 18:13 '''
import functools
def dec_arg(arg=None):
def register(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('传进的参数为 %s' % arg)
return func(*args, **kwargs)
return wrapper
return register #这是装饰器
@dec_arg(arg='aaaa')
def f1():
print("执行 %s"%f1.__name__)
def f2():
print("执行 %s" % f2.__name__)
if __name__ == '__main__':
f1()
f2_ = dec_arg(arg='bbbb')(f2)
f2_()
dec_arg(arg='cccc')(f2)()
复制代码