文章目录
一、引言
装饰器是每一个使用Python的人都会接触到的一种增强函数功能的方式,也是面试官经常会问到的知识点,这里通过一个函数运行时间的装饰器,举例说明常见的四种装饰器实现方法
装饰器的概念
装饰器是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic
装饰器实际上也是一个函数 ,这个函数以闭包的形式定义,而我们知道在Python中万物皆对象,即函数也是一个对象,而且函数对象可以被赋值给变量,所以将原函数作为变量传递给装饰器函数,就能够给原函数添加装饰器中的功能
这里举一个最简单的装饰器例子:
def decorator(func):
def wrapper(*args, **kwargs):
print("decorator sentence")
func(*args, **kwargs)
return wrapper
@decorator
def hello():
print("hello world")
hello()
>>>decorator sentence
>>>hello world
decorator
方法接收一个函数作为参数,使用@decorator
装饰hello
函数之后,相当于执行了hello = decorator(hello)
而decorator
方法return wrapper
,因此被装饰后执行的hello()
相当于执行了wrapper()
这里wrapper(*args, **kwargs)
接收所有的参数,并把参数传递给func
形参对应的函数(也就是hello
函数)执行
二、用装饰器装饰函数
1、函数装饰器
在面试中,面试官考察面试者对于装饰器的基本掌握水平,常常会让面试者手写一个简单装饰器,其中写一个打印方法执行时间的装饰器是最有可能出现的问题之一
这里就以实现一个打印方法执行时间的装饰器为例
import time
def calculate_time(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print("Function run time is: ", end - start)
return result
return wrapper
@calculate_time
def test_func(time_val):
time.sleep(time_val)
print("Function sleep %s second" % time_val)
return "end"
if __name__ == '__main__':
test_func(2)
这个例子中wrapper(*args, **kwargs)
接收任意参数,而且将函数test_func
执行结果return
,实现的是有参有返的装饰器(这里的有参有返指的是原函数有参有返)。
虽然代码最后并没有用到原test_func
的返回结果,写成有参有返只是想展现一种比较全能的装饰器格式。
结果(后面结果相同):
Function sleep 2 second
Function run time is: 2.0045318603515625
2、带参数的函数装饰器
在实际开发中,我们的装饰器往往需要接收参数,根据传入装饰器的参数不同实现不同功能,那就需要编写一个返回decorator的高阶函数,写出来会更复杂
import time
def calculate_time(time_val): # time_val为装饰器接收的参数
def decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(time_val=time_val, *args, **kwargs) # 将装饰器接收的参数作为原函数的参数
end = time.time()
print("Function run time is: ", end - start)
return result
return wrapper
return decorator
@calculate_time(2)
def test_func(time_val):
time.sleep(time_val)
print("Function sleep %s second" % time_val)
return "end"
if __name__ == '__main__':
test_func()
在这个案例中将装饰器的参数传给了原函数,因此调用函数时不需要传值。实际开发中可以根据需要使用在不同的地方
进一步的,带参数的装饰器相当于执行了hello = calculate_time(2)(test_func)
首先执行calculate_time(2)
,返回的是decorator
函数,再调用返回的decorator
函数,参数是test_func
函数,返回值最终跟无参数的装饰器,是wrapper
函数
3、类装饰器
call 方法(仿函数/函数对象)
简单来说, 就是当实例以函数的格式使用时, 调用的是__call__
方法内的函数
实现__call__
后,可以将对象当做函数一样去使用,称为仿函数或函数对象,例如
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
s = Student('Michael')
s() # self参数不要传入
>>>My name is Michael.
使用类的__call__方法实现装饰器
import time
class CalculateTime(object):
def __init__(self, func):
self.__func = func
def __call__(self, *args, **kwargs):
start = time.time()
result = self.__func(*args, **kwargs)
end = time.time()
print("Function run time is: ", end - start)
return result
@CalculateTime
def test_func(time_val):
time.sleep(time_val)
print("Function sleep %s second" % time_val)
return "end"
if __name__ == '__main__':
test_func(2)
可以看到上面的例子在类的初始化方法__init__
中将原函数传递进去,在__call__
方法中调用原函数,并将装饰器需要实现的功能写在方法内即可
执行test_func = CalculateTime(test_func)
,相当于执行了CalculateTime
装饰类的对象
4、带参数的类装饰器
使用类实现带参数的装饰器的思想与函数实现装饰器一致,同样是将装饰器的深度加深一层,这里是在__call__
方法将原函数再包一层
需要注意,类的__init__
方法接收装饰器需要的参数进行初始化,而不再接收原函数作为参数
原函数作为__call__
的形参传入,结果返回wrapper函数
import time
class CalculateTime(object):
def __init__(self, time_val):
self.__time_val = time_val
def __call__(self, func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(time_val=self.__time_val, *args, **kwargs)
end = time.time()
print("Function run time is: ", end - start)
return result
return wrapper
@CalculateTime(2)
def test_func(time_val):
time.sleep(time_val)
print("Function sleep %s second" % time_val)
return "end"
if __name__ == '__main__':
test_func()
在上面的例子中,test_func = CalculateTime(2)(test_func)
,其中先执行CalculateTime(2)
进行类的初始化,之后相当于执行__call__(test_func)
,最终返回wrapper
函数
三、用装饰器装饰类
1、函数装饰器
def decorator(cls):
def wrapper(*args, **kwargs):
print('class name:', cls.__name__)
return cls(*args, **kwargs)
return wrapper
@decorator
class Example(object):
def __init__(self, name):
self.name = name
def fun(self):
print('self.name =', self.name)
e = Example('Bryce')
e.fun()
结果(后面例子的运行结果相同):
class name: Example
self.name = Bryce
2、带参数的函数装饰器
def decorator_desc(description):
def decorator(cls):
def wrapper(*args, **kwargs):
print(description, cls.__name__)
return cls(*args, **kwargs)
return wrapper
return decorator
@decorator_desc('class name:')
class Example(object):
def __init__(self, name):
self.name = name
def fun(self):
print('self.name =', self.name)
e = Example('Bryce')
e.fun()
3、类装饰器
class DecoratorClass(object):
def __init__(self, cls):
self._cls = cls
def __call__(self, *args, **kwargs):
print('class name:', self._cls.__name__)
return self._cls(*args, **kwargs)
@DecoratorClass
class Example(object):
def __init__(self, name):
self.name = name
def fun(self):
print('self.name =', self.name)
e = Example('Bryce')
e.fun()
4、带参数的类装饰器
class DecoratorClass(object):
def __init__(self, description):
self.__description = description
def __call__(self, cls):
def wrapper(*args, **kwargs):
print(self.__description, cls.__name__)
return cls(*args, **kwargs)
return wrapper
@DecoratorClass('class name:')
class Example(object):
def __init__(self, name):
self.name = name
def fun(self):
print('self.name =', self.name)
e = Example('Bryce')
e.fun()
参考:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584
https://blog.csdn.net/ITlanyue/article/details/82147720
https://blog.csdn.net/xiemanr/article/details/72510885