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