Python 装饰器
装饰器
在Python中,函数也是一个对象,可以被当做参数传递,可以在另一个函数中调用,也可以实现函数的嵌套。
所以,利用这个特性,我们可以写出一段测试功能运行时长的代码。
import time
def function():
print("I am function!")
time.sleep(3)
print("I have finished my work!")
def index(func):
startTime = time.time()
func()
enfTime = time.time()
print("script spend time is", enfTime - startTime)
index(function)
这样的想法乍一看很好,但其实这从本质上看,不是改变了function函数的功能,而是创建了一个新的函数,而后将function融合进了这个函数里,最终实际上对function函数的一个包装,使它成为了一个新的函数,来实现功能。
那么,如果我们想要直接调用function函数就能实现像程序计时这样的功能呐?(要注意,我们做的一切都要建立在不更改function函数任何代码的前提之下。)
Python中有装饰器可以帮助我们更好的实现这样的功能。
装饰器的本质还是函数,他的作用是去装饰另外一个函数,而在程序计时这样的场景里,我们就需要根据我们要计时的程序,去编写一个计时的函数,这个计时的函数就是装饰器。
import time
def index(func):
def test_time():
print("I doing some work")
func()
print("I am end!")
return test_time
@index
def function():
print("I am function!")
time.sleep(3)
print("I have finished my work!")
function()
我们使用了装饰器的语法糖,@加函数名的形式,这其实就相当于一个index(function),但是因为他的本质是装饰,因此在function不执行的情况下,程序也不会执行。
最后我们直接调用function函数,就能顺利的完成程序计时的功能。
这看起来似乎还有一点问题,但其实是因为此处应用的场景有一点点问题,作为程序计时来说,并不需要把这个功能焊死在函数里,但是这样的使用,似乎把计时焊死在了原来的函数内部。
其实换一个应用场景就好了,例如在flask等web框架里,我们针对用户授权的时候,一个小小的装饰器就能解决。
装饰器带参数
首先是,装饰的对象带参数。
如图,尽管我已经在调用的时候加入了参数,但是最终依然报错了。
因为在装饰器中,我运行了这个函数,但是此时我是没有带参数的。
并且值得注意的是,函数的参数可以很长,因为如果我们直接写参数的话,好像有点不对劲。
于是我们简单的使用一样python中的动态参数。
这其实很简单,不用动态参数,你手动加入参数也可以。
import time
def index(func):
def test_time(*args, **kwargs):
print("I doing some work")
func(*args, **kwargs)
print("I am end!")
return test_time
@index
def function(x, y):
print("I am function!")
time.sleep(3)
print("I have finished my work!")
print(x + y)
function(2, 3)
装饰器带参数
这是另外一个问题,我们希望给装饰器带上参数,使得它使用起来更加的灵活,如果你见过flask的代码,那么就应该见过装饰器带参数的情况。、
import time
def index(url):
def test_time(func):
def Timer(*args, **kwargs):
print("I doing some work")
func(*args, **kwargs)
print("I request the ", url)
print("I am end!")
return Timer
return test_time
@index("/home")
def function(x, y):
print("I am function!")
time.sleep(3)
print("I have finished my work!")
print(x + y)
function(2, 4)
如上,这就是一个带参数的装饰器。
你可能发现了装饰器上三层的函数嵌套,并且写出了内两层的返回值,这是不可缺少的。
我们将它写成最开始的形式;
import time
def function(x, y):
print("I am function!")
time.sleep(3)
print("I have finished my work!")
print(x + y)
def index(url, func, *args, **kwargs):
startTime = time.time()
func(*args, **kwargs)
enfTime = time.time()
print("script spend time is", enfTime - startTime)
print("I request the ", url)
return index
index("home", function, 2, 5)
可以发现此时再使用这种形式,整个函数的调用就变得异常的繁琐和麻烦了起来。
类装饰器
或许叫他装饰器类更加合适,比起一般的函数形式的装饰器,
import time
class Foo(object):
def __init__(self, url):
self._url = url
def __call__(self, func):
def wrap(*args,**kwargs):
print("func is starting!")
func(*args, **kwargs)
print("I request the " + self._url)
print("func is ending!")
return wrap
@Foo("home")
def function(x, y):
print("I am function!")
time.sleep(3)
print("I have finished my work!")
print(x+y)
function(4, 6)
装饰器嵌套
class Foo01(object):
def __init__(self, url):
self._url = url
def __call__(self, func):
def wrap(*args, **kwargs):
func(*args, **kwargs)
print("I request the " + self._url)
return wrap
class Foo02(object):
def __init__(self, url):
self._url = url
def __call__(self, func):
def wrap(*args, **kwargs):
func(*args, **kwargs)
print("The IP" + self._url)
return wrap
class Foo03(object):
def __init__(self, url):
self._url = url
def __call__(self, func):
def wrap(*args, **kwargs):
func(*args, **kwargs)
print("The UA" + self._url)
return wrap
@Foo03("{'User_Agent':'*****'}")
@Foo02("192.168.121.130")
@Foo01("home")
def function(x, y):
print(x + y)
function(4, 6)
由此可以发现,装饰器的运行是按照和函数的距离来决定的,越近就越先执行。