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 的概念和使用。我們還討論了類裝飾器如何返回語句、獲取執行和檢查錯誤引數