python中的modify是什么意思_理解Python中的装饰器
⽂章先由stackoverflow上⾯的⼀个问题引起吧,如果使⽤如下的代码:
@makebold
@makeitalic
def say():
return "Hello"
打印出如下的输出:
Hello
你会怎么做?最后给出的答案是:
def makebold(fn):
def wrapped():
" + fn() + ""
return "" + fn() + "
return wrapped
def makeitalic(fn):
def wrapped():
return "" + fn() + ""
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print hello() ## 返回 hello world
现在我们来看看如何从⼀些最基础的⽅式来理解Python的装饰器。英⽂讨论参考Here。
装饰器是⼀个很著名的设计模式,经常被⽤于有切⾯需求的场景,较为经典的有插⼊⽇ 志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出⼤量函数中与函数功能本⾝⽆关的雷同代码并继续重⽤。概括的 讲,装饰器的作⽤就是为已经存在的对象添加额外的功能。
1.1. 需求是怎么来的?
装饰器的定义很是抽象,我们来看⼀个⼩例⼦。
def foo():
print 'in foo()'
foo()
这是⼀个很⽆聊的函数没错。但是突然有⼀个更⽆聊的⼈,我们称呼他为B君,说我想看看执⾏这个函数⽤了多长时间,好吧,那么我们可以这样做:
import time
def foo():
start = time.clock()
print 'in foo()'
end = time.clock()
print 'used:', end - start
foo()
很好,功能看起来⽆懈可击。可是蛋疼的B君此刻突然不想看这个函数了,他对另⼀个叫foo2的函数产⽣了更浓厚的兴趣。
怎么办呢?如果把以上新增加的代码复制到foo2⾥,这就犯了⼤忌了~复制什么的难道不是最讨厌了么!⽽且,如果B君继续看了其他的函数呢?
1.2. 以不变应万变,是变也
还记得吗,函数在Python中是⼀等公民,那么我们可以考虑重新定义⼀个函数timeit,将foo的引⽤传递给他,然后在timeit中调⽤foo并进⾏计时,这样,我们就达到了不改动foo定义的⽬的,⽽且,不论B君看了多少个函数,我们都不⽤去修改函数定义了!
import time
def foo():
print 'in foo()'
def timeit(func):
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
timeit(foo)
看起来逻辑上并没有问题,⼀切都很美好并且运作正常!……等等,我们似乎修改了调⽤部分的代码。原本我们是这样调⽤的:foo(),修改以后变成 了:timeit(foo)。这样的话,如果foo在N处都被调⽤了,你就不得不去修改这N处的代码。或者更极端的,考虑其中某处调⽤的代码⽆法修改这个 情况,⽐如:这个函数是你交给别⼈使⽤的。
1.3. 最⼤限度地少改动!
既然如此,我们就来想想办法不修改调⽤的代码;如果不修改调⽤代码,也就意味着调⽤foo()需要产⽣调⽤timeit(foo)的效果。我们可以想到将timeit赋值给foo,但是timeit似乎带有⼀个参数……想办法把参数统⼀吧!如果timeit(foo)不是直接产⽣调⽤效果,⽽是返回 ⼀个与foo参数列表⼀致的函数的话……就很好办了,将timeit(foo)的返回值赋值给foo,然后,调⽤foo()的代码完全不⽤修改!
#-*- coding: UTF-8 -*-
import time
def foo():
print 'in foo()'
# 定义⼀个计时器,传⼊⼀个,并返回另⼀个附加了计时功能的⽅法
def timeit(func):
# 定义⼀个内嵌的包装函数,给传⼊的函数加上计时功能的包装
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
# 将包装后的函数返回
return wrapper
foo = timeit(foo)
foo()
这样,⼀个简易的计时器就做好了!我们只需要在定义foo以后调⽤foo之前,加上foo = timeit(foo),就可以达到计时的⽬的,这也就是装饰器的概念,看起来像是foo被timeit装饰了。在在这个例⼦中,函数进⼊和退出时需要计 时,这被称为⼀个横切⾯(Aspect),这种编程⽅式被称为⾯向切⾯的编程(Aspect-Oriented Programming)。与传统编程习惯的从上往下执⾏⽅式相⽐较⽽⾔,像是在函数执⾏的流程中横向地插⼊了⼀段逻辑。在特定的业务领域⾥,能减少⼤量 重复代码。⾯向切⾯编程还有相当多的术语,这⾥就不多做介绍,感兴趣的话可以去相关的资料。
这个例⼦仅⽤于演⽰,并没有考虑foo带有参数和有返回值的情况,完善它的重任就交给你了 :)
上⾯这段代码看起来似乎已经不能再精简了,Python于是提供了⼀个语法糖来降低字符输⼊量。
import time
def timeit(func):
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
return wrapper
@timeit
def foo():
print 'in foo()'
foo()
重点关注第11⾏的@timeit,在定义上加上这⼀⾏与另外写foo = timeit(foo)完全等价,千万不要以为@有另外的魔⼒。除了字符输⼊少了⼀些,还有⼀个额外的好处:这样看上去更有装饰器的感觉。
-------------------
要理解python的装饰器,我们⾸先必须明⽩在Python中函数也是被视为对象。这⼀点很重要。先看⼀个例⼦:
def shout(word="yes") :
return word.capitalize()+" !"
print shout()
# 输出 : 'Yes !'
# 作为⼀个对象,你可以把函数赋给任何其他对象变量
scream = shout
# 注意我们没有使⽤圆括号,因为我们不是在调⽤函数
java python是什么意思
# 我们把函数shout赋给scream,也就是说你可以通过scream调⽤shout
print scream()
# 输出 : 'Yes !'
# 还有,你可以删除旧的名字shout,但是你仍然可以通过scream来访问该函数
del shout
try :
print shout()
except NameError, e :
print e
#输出 : "name 'shout' is not defined"
print scream()
# 输出 : 'Yes !'
我们暂且把这个话题放旁边,我们先看看python另外⼀个很有意思的属性:可以在函数中定义函数:def talk() :
# 你可以在talk中定义另外⼀个函数
def whisper(word="yes") :
return word.lower()+"...";
# ... 并且⽴马使⽤它
print whisper()
# 你每次调⽤'talk',定义在talk⾥⾯的whisper同样也会被调⽤
talk()
# 输出 :
#
# 但是"whisper" 不会单独存在:
try :
print whisper()
except NameError, e :
print e
#输出 : "name 'whisper' is not defined"*
函数引⽤
从以上两个例⼦我们可以得出,函数既然作为⼀个对象,因此:
1. 其可以被赋给其他变量
2. 其可以被定义在另外⼀个函数内
这也就是说,函数可以返回⼀个函数,看下⾯的例⼦:
def getTalk(type="shout") :
# 我们定义另外⼀个函数
def shout(word="yes") :
return word.capitalize()+" !"
def whisper(word="yes") :
return word.lower()+"...";
# 然后我们返回其中⼀个
if type == "shout" :
# 我们没有使⽤(),因为我们不是在调⽤该函数
# 我们是在返回该函数
return shout
else :
return whisper
# 然后怎么使⽤呢 ?
# 把该函数赋予某个变量
talk = getTalk()
# 这⾥你可以看到talk其实是⼀个函数对象:
print talk
#输出 :
# 该对象由函数返回的其中⼀个对象:
print talk()
# 或者你可以直接如下调⽤ :
print getTalk("whisper")()
#输出 :
还有,既然可以返回⼀个函数,我们可以把它作为参数传递给函数:
def doSomethingBefore(func) :
print "I do something before then I call the function you gave me"
print func()
doSomethingBefore(scream)
#输出 :
#I do something before then I call the function you gave me
#Yes !
这⾥你已经⾜够能理解装饰器了,其他它可被视为封装器。也就是说,它能够让你在装饰前后执⾏代码⽽⽆须改变函数本⾝内容。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。