内容简介:在python中,这里now函数被赋值给了f变量,调用now函数,其实就是进行f的调用。平时来看,函数一般都有入参(即函数的局部变量),而因为变量能代表函数,故函数入参为函数也就好理解了。故对实现了装饰器的函数,它的入参为函数也就不奇怪了。在python中当一个函数a就将另一个函数b作为参数,那么这个函数a就称之为可以看到函数log它最终的返回值是一个函数wrapper。在python中,返回函数指:“对于一个函数c,它最后的返回值是一个函数d,那么函数c就是一个返回函数”。返回函数的好处就是当调用c函数时
在 python 中, “函数也是一种对象” ,这句话是理解装饰器的基础。因为函数是一种对象,并且对象能被赋值给变量,所以函数就能被赋值给变量。例如:
>>> def now(): ... print('2015-3-25') ... >>> f = now >>> f() 复制代码
这里now函数被赋值给了f变量,调用now函数,其实就是进行f的调用。平时来看,函数一般都有入参(即函数的局部变量),而因为变量能代表函数,故函数入参为函数也就好理解了。故对实现了装饰器的函数,它的入参为函数也就不奇怪了。在python中当一个函数a就将另一个函数b作为参数,那么这个函数a就称之为 高阶函数 。而上面的函数log将func函数作入参,那么log就是一个高阶函数。
返回函数
可以看到函数log它最终的返回值是一个函数wrapper。在python中,返回函数指:“对于一个函数c,它最后的返回值是一个函数d,那么函数c就是一个返回函数”。返回函数的好处就是当调用c函数时不用立即执行c中的所有逻辑,我们可以看下面的例子:
def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum 复制代码
函数lazy_sum实现了一个求和的功能,当我们调用lazy_sum时,其返回的不是直接的求和值,而是一个函数sum,
>>> f = lazy_sum(1, 3, 5, 7, 9) >>> f <function lazy_sum.<locals>.sum at 0x101c6ed90> 复制代码
这里f变量其实就是sum函数,所以要求和的值时,调用f才可以。
>>> f() 25 复制代码
“当有一个函数h,它定义了内部函数g,最后返回了函数g,并且在该过程中g还使用了h函数的局部变量,那么这种程序python中称之为 闭包 ”。根据闭包定义,其实它也是一种返回函数,只不过它的内部函数使用了外部函数的局部变量(入参)。
装饰器理解
装饰器剖析
有了前面的知识储备,再来看最开始实现了装饰器功能的函数log:
def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper 复制代码
可以看到log函数的入参为func函数,根据定义,log函数就是一个高阶函数。再看log函数的返回值为函数wrapper,根据定义,log函数也是一个返回函数。如果把func看作一个变量,那么内部的函数wrapper使用了外部函数log的变量,根据定义,这个log函数它的程序也是一个闭包。 此刻我们对于实现装饰器功能的程序就有了一个整体认识,它既是一个高阶函数也是一个返回函数。当然整体上看它也是一个闭包。
装饰器的运行过程
在python中,当我们使用装饰器:
@log def now(): print('2015-3-25') now() 复制代码
now()运行过程,实际上相当于执行了语句:
now = log(now) 复制代码
即 用了一个同原来now函数名字相同的变量now,(为了好区分,我们后面称变量now为now'吧。)来表示log函数的返回值。 这里log函数的返回值是函数wrapper,前面讲过函数是变量,变量能表示函数,此时now'变量表示了函数wrapper,即 now' = wrapper ,now()的执行相当于执行了wrapper函数,即 now'() 。 表面上看加了装饰器的now函数,其调用跟不加装饰器正常调用now函数是一样的,但经过上面我分析实际上并并不是,它实际上执行的是 now' = log(now); now'() 这两步。那句广告语“看起来一样一样,实际就是不一样...”,哈哈,说的就是这个过程。
带参数的装饰器
有时候我们给某个函数动态添加了功能,但又希望我们添加的功能对于我们的需求是可定制的。因此则有必要给装饰器添加参数变量。python中给装饰器添加参数的实现是这样的:
def log2(text): def log(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return log 复制代码
用法则如下:
@log2('execute') def now(): print('2015-3-25') 复制代码
>>> now() execute now(): 2015-3-25 复制代码
直观上看,其实就是给log函数外部再加了一层函数。根据前面的函数定义,不难看出log2是一个返回函数。而now()的运行过程则相当于执行了下面的语句:
now = log2('execute')(now) 复制代码
即 用了一个同原来now函数名字相同的变量now,(为了好区分,我们也称变量now为now'吧。)来表示log函数的返回值 。这里为什么now'表示的是log函数的返回值,而不是log2的返回值?相信聪明的你一眼也能得到答案哈!我们看, log2('execute') 这个函数的执行,实际上是返回了log函数,我们假设将返回的log函数赋值给变量f,那么 log2('execute')(now) 的执行其实就相当于 f(now) 的执行,而 f(now) 的执行,其返回值就是log函数的返回值即wrapper函数。所以这里now'其实表示的是wrapper函数。 可以看到无论是带参数还是不带参的装饰器,其表面上看now()的执行是执行了now,实际上都是执行wrapper函数。不相信的小伙伴可以打印下now的名称(即now.__name__)试一下,其值都是wrapper。表面上看python将now()的执行伪装了,但终究“狐狸的尾巴藏不住啊”,打印下它的函数名称,它就显露原形了。那么为了在平时使用中保持一致性,即加不加装饰器,打印“now.__name__”时都可以得到now函数的原始名字now',python也给了我们方法,即使用Python内置的 functools.wraps 即可。不带参和带参的方法各如下:
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper 复制代码
import functools def log2(text): def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return log 复制代码
可以看到,其@functools.wraps(func)语句都是在wrapper函数上面加的。
总结
当然python的装饰器使用不止这些。这里只是最基本的理解(若有理解错的,请各位大佬指正哈)。对装饰器的理解首先是要知道 函数也是对象 ,其次只要弄清楚一些函数定义就可以了。当然这里还是得感谢 廖雪峰大神的python3教程 ,正是因为他那清晰透彻的教程,才使我理解装饰器的概念。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 装饰器与元数据反射(1)方法装饰器
- 草根学Python(十六) 装饰器(逐步演化成装饰器)
- 一文读懂 JS 装饰器,这是一个会打扮的装饰器
- 装饰器与元数据反射(2)属与类性装饰器
- Python装饰器
- python--装饰器详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
DOM Scripting
Jeremy Keith / friendsofED / 2010-12 / GBP 27.50
There are three main technologies married together to create usable, standards-compliant web designs: XHTML for data structure, Cascading Style Sheets for styling your data, and JavaScript for adding ......一起来看看 《DOM Scripting》 这本书的介绍吧!