参考链接:Python 函数装饰器
我认为python中的装饰器是一个很厉害的功能,他能瞬间提升代码的逼格,但对于我这样的小白来说,别说为所欲为的使用了,就连简单的尝试一下,却也是难于登天。经过长达半年的努力,我终于找到了大部分装饰器的介绍信息,鲁迅曾经说过,良好的开始就代表了成功的一半,在我看来,鲁迅分明还是太保守,良好的开端无疑代表你已经成功了。比如接下来我们只需要Ctrl+C+V,就可以完成装饰器的学习了,亲爱的小朋友们,你们学会了吗?
给大人看的分割线
装饰器是用来给已经定义好的函数增加功能用的,能在方法运行期间动态增加方法的功能
1)不会改变原来的代码结构。
为什么这么说呢,因为相比于另一种方法:只将函数作为变量传入另一个函数从而来减少重合,使用装饰器并不需要将函数调用改写
#定义这个函数来为类似foo()的需要去增加loging.warn()的全部函数增添这个loging.warn()的功能 #假如有许多函数和foo()一样,都需要增加loging.warn()这行代码,我们就可以定义一个函数,然后将每个需要增加功能的函数作为参数传入 #但是这样必须在用use_logging(foo)去替换原有的foo()这就改变了函数的调用,破坏了原有的代码结构 def use_logging(func): logging.warn("%s is running" % func.__name__) func() def foo(): print('i am foo') use_logging(foo)
2)装饰器在不修改函数代码的前提下影响代码的功能;调用python中的函数可以作为变量去传递,基于这一特性,我们可以构建出自己的装饰器;
先来了解一些基础知识:
1)我们可以将函数作为变量传递。要注意括号,加了括号就会运行函数。
2)在函数的内部还可以嵌套函数
def hi(name="yasoob"): print("现在在hi()里面运行") def greet(): return "现在在greet()里面运行" def welcome(): return "现在在welcom()里面运行" print(greet()) print(welcome()) print("现在你返回到了hi()里面") hi()
# 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。 # 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如
结果:
现在在hi()里面运行 现在在greet()里面运行 现在在welcom()里面运行 现在你返回到了hi()里面
3)从函数中返回函数
def hi(name="yasoob"): def greet(): return "now you are in the greet() function" def welcome(): return "now you are in the welcome() function" if name == "yasoob": return greet else: return welcome a = hi() print(a) #outputs: <function greet at 0x7f2143c01500> #上面清晰地展示了`a`现在指向到hi()函数中的greet()函数 #现在试试这个 print(a()) #outputs: now you are in the greet() function
注意下面的 hi()返回的值是greet,加上后面的括号时,就成为了greet()随即开始运行
hi()() #这会输出 now you are in the greet() function。
4)将函数作为参数传递给另外一个函数
def hi(): return "hi yasoob!" def doSomethingBeforeHi(func): print("I am doing some boring work before executing hi()") print(func()) doSomethingBeforeHi(hi) #注意不要加括号 #outputs:I am doing some boring work before executing hi() # hi yasoob!
第一个修饰器
def a_new_decorator(a_func): def wrapTheFunction(): print('我在a_func执行前做一些事情') a_func() print('我在a_func执行后做一些事情') return wrapTheFunction #记得把这个内定义的函数返回 def a_function_requiring_decoration(): #定义一个函数,等会用修饰器来修饰 print('我是需要用修饰器修饰的函数') #使用修饰器 a_function_requiring_decoration=a_new_decorator(a_function_requiring_decoration) a_function_requiring_decoration()
结果:
我在a_func执行前做一些事情 我是需要用修饰器修饰的函数 我在a_func执行后做一些事情
真正的装饰器:decorator
另一种生成被装饰函数的方法:用@
补充----------------
装饰器,只能在函数运行前做一些事情,也能在函数运行后执行,不过要是有返回结果的就不能在其后执行了,因为有return语句但是可以将装饰器写成这样
def log(parameter1): def decorator(func): @functools.wraps(func) def wrapper(*args,**kw): s=func(*args,**kw) print('函数%s()运行消耗时间%.11f'%(func.__name__,parameter1)) return s#前面执行,这里返回 return wrapper return decorator
补充结束------------------
先了解一下@语句
最简单的装饰器
如果被装饰的函数运行时不需要参数,我们可以这样定义装饰器
def metric(fn): print('%s executed in %s ms' % (fn.__name__, 10.24)) return fn @metric def customize(a,b): print("我是被装饰的函数,运行结果%d"%a+b) customize(1,3) #输出 customize executed in 10.24 ms 我是被装饰的函数,运行结果4
把@metric
放到customize()
函数的定义处,相当于执行了语句:
customize=metric(customize)
由于metric()
是一个decorator,返回一个函数,所以,原来的customize()
函数仍然存在,只是现在同名的customize变量指向了新的函数,于是调用customize()
将执行新函数,即在metric()
函数中返回的wrapper中返回的customize
函数。
普遍使用的装饰器的格式
上面的只是最简单的,我们通常使用装饰器的时候都需要被装饰的函数的运行结果,当被装饰的函数需要参数时,使用上面的定义就不行了:
def a_new_decorator(a_func): def wrapTheFunction(): print('我在a_func执行前做一些事情') a_func() print('我在a_func执行后做一些事情') return wrapTheFunction #记得把这个内定义的函数返回 #不同之处在这里 @a_new_decorator def a_function_requiring_decoration(): #定义一个函数,等会用修饰器来修饰 print('我是需要用修饰器修饰的函数') a_function_requiring_decoration()
把@log
放到now()
函数的定义处,相当于执行了语句:
now = log(now)
由于log()
是一个decorator,返回一个函数,所以,原来的now()
函数仍然存在,只是现在同名的now
变量指向了新的函数,于是调用now()
将执行新函数,即在log()
函数中返回的wrapper()
函数。
wrapper()
函数的参数定义是(*args, **kw)
,因此,wrapper()
函数可以接受任意参数的调用。在wrapper()
函数内,首先打印日志,再紧接着调用原始函数。
解决被修饰的函数会被重命名问题的wraps函数
使用上面会出现一个问题,获取_name_时会返回被修饰器内置函数的名字
print(a_function_requiring_decoration.__name__) # Output: wrapTheFunction 应该返回a_function_requiring_decoration
之所以返回的是修饰器里内置的名字,是因为它会重写函数的名字和注释文档
而python提供了 functools.wraps() 来解决这个办法,以如下代码为例说明 functolls.wraps 使用方法
from functools import wraps def a_new_decorator(a_func): @wraps(a_func) def wrapTheFunction(): ---snip---
return a_func return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): ---snip-- print(a_function_requiring_decoration.__name__) # Output: a_function_requiring_decoration
@wraps 接受一个被修饰函数的名字,并加入了复制函数名称、注释文档、参数列表等功能,这让我们能在修饰器里访问函数被修饰之前的属性
怎样为修饰器传入参数:
假如 a_function_requiring_decoration() 需要参数
def a_function_requiring_decoration(name): --snip--
我们需要在定义 wrapTheFunction() 时指定参数即:
def wrapTheFunction(name): ---snip--- return a_func(name)
当装饰器不知道要装饰的函数有多少个参数时,可以用 *args 来代替
def wrapTheFunction(*args): ---snip--- return a_func(*args)
当需要关键字参数时
def a_function_requiring_decoration(name, age=None, height=None): print("I am %s, age %s, height %s" % (name, age, height))
可以这样
def wrapTheFunction(*args, **kwargs): # args是一个数组,kwargs一个字典 logging.warn("%s is running" % func.__name__) return func(*args, **kwargs)
和两层嵌套的decorator相比,3层嵌套的效果是这样的:
def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator
用法
@log('execute') def now(): print('2015-3-25')
和两层嵌套的decorator相比,3层嵌套的效果是这样的:
>>> now = log('execute')(now)
我们来剖析上面的语句,首先执行log('execute')
,返回的是decorator
函数,再调用返回的函数,参数是now
函数,返回值最终是wrapper
函数。
原文链接 >>>点击<<<