Python 中的类装饰器
-
Python 中的类
装饰器
- 在 Python 中扩展代码的功能
-
Python 中带有类
装饰器
的参数 -
在 Python 中使用
*Args
和**Kwargs
作为参数 -
有返回语句的
装饰器
- 在 Python 中获取执行时间
-
在 Python 中使用类
装饰器
检查错误参数
- 结论
在 Python 中,我们可以在不修改函数或类的情况下扩展其行为。
我们可以在装饰器
的帮助下将函数包装在其他函数中,以向现有的类或函数添加一些功能。
Python 中的类装饰器
Decorator
是 Python 中的一个工具,可以让程序员修改类或函数的行为。
我们可以将装饰器
可视化为一个三步过程,其中:
- 我们给
decorator
一些函数作为输入。 装饰器
用于添加功能。decorator
返回具有附加用法的函数。
例如,我们有功能 A,我们想添加功能而不永久修改它们。我们可以通过使用 __call__
方法将装饰器
用作一个类。
Callable
是可以实现 __call__
方法的任何对象。decorator
是可以返回 callable
的 callable
。
在外行语言中,如果一个对象类似于一个函数,函数 decorator
也应该返回一个类似于函数的对象。这是一个使用 __call___
方法的示例。
#Use of the __call__ method in Python
class MyDemoDecorator:
def __init__(self, func):
self.func = func
def __call__(self):
#Code before the function call
self.func()
#Code after the function call
# adding the decorator as class
@MyDemoDecorator
def func():
print("Learning!")
func()
输出:
Learning
当我们使用类装饰一个函数时,我们使该函数成为装饰类的实例
。
我们用于装饰函数的类可以有参数,但如果我们不传递任何参数,该类将回退到 default
值。
在 Python 中扩展代码的功能
我们有一个函数 mul_nums()
,它将两个数字相乘并返回乘积作为输出。现在,我们希望这个函数也返回产品和产品的立方体。
计算产品立方体的部分是一个额外的函数,我们不会添加到源代码中。相反,我们将使用类装饰器
来实现这个附加功能。
我们在下面的代码块中使用 @Cube
用类装饰函数。
例子:
class Cube(object):
def __init__(self, args):
self.args = args
def __call__(self, x, y):
res = self._args(x,y)
return res*res*res
@Cube
def mul_nums(x, y):
return x * y
print(mul_nums)
print(mul_nums(4,3))
输出:
1728
类中的 init
构造函数自动接收函数作为第一个参数。该函数被设置为对象内部的属性。
因此,当我们打印 mul_nums
时,我们可以将函数 mul_nums()
视为 Cube
类的实例。
在 __call__()
方法中,我们调用 mul_nums
函数,其中发生了结果的乘法和立方。在找到它的 cube
后返回该值。
我们可以在此代码中添加另外一个函数。假设我们为我们的 cube
对象提供了一些立方值的记忆。
为此,我们使用空列表
并将其设置为与对象内存对应的属性。每次我们调用装饰函数时,我们也会将它附加到这个列表中。
最后,我们定义方法 mem
,它从列表中返回存储的值。
例子:
class Cube(object):
def __init__(self, args):
self._args = args
self._mem = []
def __call__(self, x, y):
res = self._args(x, y)
self._mem.append(res * res * res)
return res * res * res
def mem(self):
return self._mem
@Cube
def mul_nums(x, y):
return x * y
print(mul_nums)
print(mul_nums(4,3))
print(mul_nums(2,3))
print(mul_nums(5,2))
print(mul_nums.mem())
输出:
1728
Python 中带有类装饰器
的参数
decorator
类有两种类型。一个接受参数,另一个不接受。
两种类型都可以正常工作,但是可以接受参数的类 decorator
更加灵活和高效。
让我们一一看看这两种情况。这次我们将看一个场景,我们定义了一个函数 add_num()
,它将两个数字相加。
然后,我们将使用类 decorator
的概念和 add_num()
的功能来获得结果的力量。在下面的示例中,类 decorator
采用一个参数。
class Power(object):
def __init__(self, args):
self._args = args
def __call__(self, *param_arg):
if len(param_arg) == 1:
def wrap(x, y):
res = param_arg[0](x, y)
return res**self._args
return wrap
else:
exponent = 2
res = self._args(param_arg[0], param_arg[1])
return res ** exponent
@Power(2)
def add_num(x, y):
return x + y
print(add_num(4,3))
输出:
49
在这里,init
函数没有将函数作为参数。相反,我们传递给类 decorator
的参数转到 init
构造函数。
我们在此处作为参数传递的值 2
被保存为属性。稍后,当我们定义 __call__
方法时,该函数是唯一传递的参数。
请注意,如果我们传递给 __call__
方法的参数长度为 1,则该方法返回 wrap
函数。将星号 * 与 param_arg
一起使用是为了允许可变数量的参数。
现在让我们看看另一种情况,我们不向类 decorator
传递任何参数。
class Power(object):
def __init__(self, args):
self._args = args
def __call__(self, *param_arg):
if len(param_arg) == 1:
def wrap(x, y):
res = param_arg[0](x, y)
return res ** self._args
return wrap
else:
exponent = 2
res = self._args(param_arg[0], param_arg[1])
return res ** exponent
@Power
def add_num(x, y):
return x + y
print(add_num(4,3))
输出:
49
由于没有参数传递给类 decorator
,init
构造函数获取一个函数作为第一个参数。调用修饰函数会导致第一个条件失败,因此会执行 else
块。
在 else
块内,设置了 default
值。使用这个 default
值,我们得到 resultant
值。
在 Python 中使用 *Args
和 **Kwargs
作为参数
在上面的例子中,__call__
函数有一个参数。使用类 decorator
的另一种方法是在此函数中传递参数*args
和**kwargs
。
例子:
class MyDemoDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
#code before the function call
self.func(*args, **kwargs)
#code after the function call
# adding class decorator to the function
@MyDemoDecorator
def func(name, msg ='Hey there'):
print("{}, {}".format(msg, name))
func("we are learning decorators", "hey there")
输出:
hey there, we are learning decorators
有返回语句的装饰器
让我们使用确实返回一些值的函数。
在这种情况下,我们使用 return
声明。
例子:
#decorator having a return statement
class DemoDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# code before function call
res = self.func(*args, **kwargs)
# code after the function call
return res
# adding class decorator to the function
@DemoDecorator
def cube(n):
print("The given number is:", n)
return n*n*n
print("Cube of the given number is:", cube(11))
输出:
The given number is: 11
Cube of the given number is: 1331
在 Python 中获取执行时间
我们可以使用类 decorator
打印程序的执行时间。将 __call__()
函数与 time 模块一起使用。
例子:
#using class decorator to get the execution time of a program
#import the time module
from time import time
class Execution_Time:
def __init__(self, func):
self.funct = func
def __call__(self, *args, **kwargs):
start_time = time()
res = self.funct(*args, **kwargs)
stop_time = time()
print("The execution of this program took {} seconds".format(stop_time-start_time))
return res
# adding decorator to a function
@Execution_Time
def demo_function(delay):
from time import sleep
#delaying the time
sleep(delay)
demo_function(3)
输出:
The execution of this program took 3.004281759262085 seconds
在 Python 中使用类装饰器
检查错误参数
装饰器
类的用途之一是在执行之前检查函数的参数
。它可以防止函数重载,并且只存储逻辑和最必要的语句。
例子:
# use class decorator to check error parameter
class CheckError:
def __init__(self, func):
self.func = func
def __call__(self, *params):
if any([isinstance(i, str) for i in params]):
raise TypeError("Parameter is a string and it ain't possible!!")
else:
return self.func(*params)
@CheckError
def add(*numbers):
return sum(numbers)
# calling function with integers
print(add(3,5,2))
# calling function with a string in between
print(add(3, '5', 2))
输出:
10
TypeError: Parameter is a string and it ain't possible!!
结论
我们讨论了 Python 类 decorators
的概念和使用。我们还讨论了类装饰器
如何返回语句、获取执行和检查错误参数
。