在Python中装饰器的作用就是在不改变原来函数代码的情况下,扩展函数功能的一种函数,因此,装饰器本身也是一种函数,因为它本身返回的就是一个函数。
最朴素的装饰器
首先,我们定义一个函数,并在这函数里头嵌入计时的功能
这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。
所以这里装饰器就像一个注入符号@:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。
import time
def deco(func):
def wrapper():
startTime = time.time()
func()
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapper
@deco
def func():
print("hello")
time.sleep(1)
print("world")
test = func
test()
在使用装饰器中,我们可以通过在函数上方写入@deco的方法调用,也可以通过func = deco(func)的方式使用。
嵌套装饰器
如果我们想使用多个装饰器来装饰同一个函数(也就是一个函数被赋予多个功能)这也是可以实现:
import time
def deco01(func):
def wrapper(*args, **kwargs):
print("this is deco01")
startTime = time.time()
func(*args, **kwargs)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
print("deco01 end here")
return wrapper
def deco02(func):
def wrapper(*args, **kwargs):
print("this is deco02")
func(*args, **kwargs)
print("deco02 end here")
return wrapper
@deco01
@deco02
def func(a,b):
print("hello,here is a func for add :")
time.sleep(1)
print("result is %d" %(a+b))
f = func
f(3,4)
注意的是:当多个装饰器执行的顺序就是从最后一个装饰器开始,执行到第一个装饰器,再执行函数本身。
带参数的装饰器
当我们需要在装饰器中加入参数,注意这不是在被装饰的函数中加入参数。我们可以采用多一个闭包,也就是多一层的嵌套:
def outermost(*args):
def out(func):
print ("装饰器参数{}".format(args))
def inner(*args):
print("innet start")
func(*args)
print ("inner end")
return inner
return out
@outermost("我是装饰器参数")
def myfun(*args):
print ("试试装饰器和函数都带参数的情况,被装饰的函数参数{}".format(args))
myfun("我是被装饰的参数")
内置的装饰器
在Python中,普通的一个类也是一个对象,所以当我们直接使用这一个类的时候可以不去实例化一个对象而直接通过类本身去调用它其中的方法。
class test(object):
def fun1(self, n):
print("self:", self)
@classmethod
def fun2(cls, n):
print("cls:", cls)
@staticmethod
def fun3(n):
pass
a = test()
a.fun1(1) # self: <__main__.A object at 0x000001E596E41A90>
A.fun2(1) # cls: <class '__main__.A'>
A.fun3(1)
可看出来,当我们直接用staticmethod这个装饰器修饰类中方法的时候,可以不传入self这个参数,也可以不实例化一个对象出来去调用其中的方法;这就好像C++中的static成员、函数,它属于类,而不属于任何一个对象。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护,节省了实例化对象的开销成。
classmethod类似,也可以节省实例化对象的开销成本,它也不需要传入self这个参数,但是它需要传入cls的这个参数进去。当使用了@classmethod的时候,这个方法就默认为类方法,可以通过类直接去调用。
当需要使用到类中属性的时候,就使用classmethod,如果没有涉及到类中属性问题的时候,就使用staticmethod这个方法。