簡體   English   中英

如何調用其他class的裝飾器(實例方法)?

[英]How to call a decorator (instance method) of other class?

我有一個名為RMath()的 class ,它需要 2 個值ab 然后是一個名為add()的 function ,它總結了這些ab 現在我想記錄計算時間。 我決定在這里使用裝飾器而不觸及原來的 function。 同時,我不想在同一個 class 中編寫這個裝飾器,所以我創建了一個新的 class 作為Handler ,並創建了一個實例方法作為timeit 但是當我嘗試將此裝飾器用作@Handler.timeit時,它會引發錯誤TypeError: timeit() missing 1 required positional argument: 'func'

import time

class Handlers():

    def timeit(self, func):
        def timefunc():
            start = time.time()
            func()
            end = time.time()
            print('Time taken: ', end - start)
        return timefunc()

class RMath():
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @Handlers.timeit
    def add(self):
        try:
            self.a + self.b
        except Exception as e:
            return e

a = 10
b = 20
m = RMath(a, b)
c = m.add()
print(c)

至少從錯誤中我可以看到@Handlers.timeit存在一些問題

有人可以幫我理解這個問題嗎?

即使我將 self 從裝飾器中刪除為def timeit(func):它會拋出錯誤: TypeError: add() missing 1 required positional argument: 'self'

在這種情況下,您可以使用@classmethod@staticmethod 這是我的處理方法:

from functools import wraps
from time import time


class Handlers:

    @staticmethod
    def timeit(func):

        @wraps(func)
        def timed_func(*args, **kwargs):
            start = time()
            ret = func(*args, **kwargs)
            end = time()
            print(f'[{func.__qualname__}] Time taken: {end - start}')

            return ret

        return timed_func


class RMath:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @Handlers.timeit
    def add(self):
        try:
            # add a `return` here!
            return self.a + self.b
        except Exception as e:
            return e


a = 10
b = 20
m = RMath(a, b)
c = m.add()
print(c)

出去:

[RMath.add] Time taken: 3.0994415283203125e-06
30

更新: *args, **kwargs*是一種通用方法來處理帶有任意數量參數的 function,但是如果您明確地只想支持RMath.add()方法 - function 采用單個參數self - 您可以使用稍微不同的方法,如下所示:

@wraps(func)
def timed_func(self):
    start = time()
    ret = func(self)
    end = time()
    print(f'[{func.__qualname__}] Time taken: {end - start}')

    return ret

有許多較小的事情需要修復才能完成這項工作。 您必須從裝飾器返回內部 function,而不是調用 function 的結果。 You also have to marshal the arguments given to the original function through your function - and you need to return the result from your wrapped function after computing the time spent.

而且由於您從未創建Handler實例,因此您應該將包裝 function 定義為 static 方法(即使您不這樣做,它仍然可以工作,但不會有self )。

import time

class Handlers():
    # make sure we don't require an instance
    @staticmethod
    def timeit(func):
        def timefunc(*args, **kwargs):
            #        ^--- needs to accept and marshal the arguments
            start = time.time()
            result = func(*args, **kwargs)
            # ^- keep result ^--- marshal the arguments
            end = time.time()
            print('Time taken: ', end - start)
            return result
            # ^- return the result of calling the wrapped function

        return timefunc
        # ^-- return the function, not the result of calling the function


class RMath():
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @Handlers.timeit
    def add(self):
        try:
            return self.a + self.b
            # ^--- needs to return the result of the addition
        except Exception as e:
            return e

a = 10
b = 20
m = RMath(a, b)
c = m.add()
print(c)

正如現在所寫,您需要一個Handlers實例來運行Handlers.timeit() ,因此您需要實例化 class 並使用該實例的timeit()方法來裝飾其他類中的方法。

但我認為您想要做的是使用 class 作為命名空間來保存方法。 在這種情況下,您可以使用@staticmethod裝飾方法。 畢竟,該方法實際上並未使用 class 的實例。

class Handlers():

    @staticmethod
    def timeit(func):
        def timefunc():
            start = time.time()
            func()
            end = time.time()
            print('Time taken: ', end - start)
        return timefunc()

或者您可以將它用作任何 class 之外的普通 function,或者(可能是最好的主意)將它放在您導入的單獨模塊中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM