Python 中的类装饰器

Shikha Chaudhary 2023年10月10日
  1. Python 中的类装饰器
  2. 在 Python 中扩展代码的功能
  3. Python 中带有类装饰器的参数
  4. 在 Python 中使用 *Args**Kwargs 作为参数
  5. 有返回语句的装饰器
  6. 在 Python 中获取执行时间
  7. 在 Python 中使用类装饰器检查错误参数
  8. 结论
Python 中的类装饰器

在 Python 中,我们可以在不修改函数或类的情况下扩展其行为。

我们可以在装饰器的帮助下将函数包装在其他函数中,以向现有的类或函数添加一些功能。

Python 中的类装饰器

Decorator 是 Python 中的一个工具,可以让程序员修改类或函数的行为。

我们可以将装饰器可视化为一个三步过程,其中:

  1. 我们给 decorator 一些函数作为输入。
  2. 装饰器用于添加功能。
  3. decorator 返回具有附加用法的函数。

例如,我们有功能 A,我们想添加功能而不永久修改它们。我们可以通过使用 __call__ 方法将装饰器用作一个类。

Callable 是可以实现 __call__ 方法的任何对象。decorator 是可以返回 callablecallable

在外行语言中,如果一个对象类似于一个函数,函数 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

由于没有参数传递给类 decoratorinit 构造函数获取一个函数作为第一个参数。调用修饰函数会导致第一个条件失败,因此会执行 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 的概念和使用。我们还讨论了类装饰器如何返回语句、获取执行和检查错误参数