装饰器主要用途是包装另一个函数或者类。这种包装主要是为了透明地修改或者增强被包装对象的行为。装饰器的语法是特殊符号@——这是《Python参考手册》里面描述的。
装饰器其实就是一个函数,这个函数将另一个函数或者类作为参数,并返回经过处理的类或者函数。下面讨论一下装饰器的具体用法,即大家在实际想要使用装饰器的时候到底怎么写呢。
首先我想说的是当用装饰器函数去装饰一个函数的时候可以将装饰器分为是否接受参数,什么意思?请看下面的例子
@trace
def squre(x):
return x*x
这种就是没有接受参数的装饰器
@trace(1)
def squre(x):
return x*x
上面这种情况就是接受参数的
上面这两种情况表示什么意思呢,《Python参考手册》就说的很好,其分别对应下面的代码
def square(x):
return x*x
square=trace(square)#先调用trace函数,注意square名字现在指向了经过装饰的square函数,原来的square函数对象被也被保存起来了,至于保存在哪儿了,为了不让读者绕晕,这里暂时不扯了
def square(x):
return x*x
temp=trace(1)
square=temp(square)
从上面两段代码的第一个代码可以看出来,如果想要定义一个装饰器函数去修饰另一个函数,必须要将函数对象作为返回值,而且要将被装饰的函数作为参数。于是装饰器函数就很好写了,请看下面的代码
def trace(func): #接受一个函数对象作为参数
def callf(*args,**kv): #这里用位置参数和键参数来接受无法预知的函数对象的参数
return func(*args,**kv) #顺理成章地将位置参数和键参数传递给函数对象
return callf #返回经过装饰的函数对象
但是当你刚刚说的个条件用在带参数的装饰器就说不通了,带参数的装饰器很显然没有接受一个函数对象作为参数。但是有一点是肯定的就是必须返回一个被装饰了的函数对象,但是我们也可以使用逆向思维的方法,根据《Python参考手册》给出的装饰器的等价代码去实现一个装饰器
def trace(num):
if num:
def func(fun):#作为返回的中间存储的temp函数对象
def inner(*args,**kv):#这里就不带参数的装饰器的实现方法
print "Hello world"
return fun(*args,**kv)
return inner
return func
现在把写一个函数的装饰器的方法整理一下:
1、不带参的装饰器
a)将被装饰的函数对象作为参数(只接受一个函数对象作为参数)
b)在装饰函数中定义一个包装函数
c)在包装函数中适当的调用被装饰的函数
d)将这个装饰函数对象返回
2、带参的装饰器
a)在原来的不带参的装饰器的基础上再定义一个外层的处理参数的函数,内层函数会默认接受一个被装饰的函数对象作为参数
为了证明上述逆向思维的正确性,我现在举几个异想天开的例子去针对带参的装饰器
1)内层函数不光接受一个函数作为参数
def trace(switch):
if switch:
def func(fun,a)#报错中的113行
def inner(*args,**kv):
print 'Hello world'
return fun(*args,**kv)
return inner
return func
@trace(1)
def fun(a):
return a*a
在Python的shell中运行时报错:
Traceback (most recent call last):
File "<pyshell#113>", line 1, in <module>
@trace(1)
TypeError: func() takes exactly 2 arguments (1 given)###产生这种错位因为func只能接受一个参数,因为解释器只会传一个参数给他