简体   繁体   English

如何调用其他class的装饰器(实例方法)?

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

I have a class called RMath() which takes 2 values a and b .我有一个名为RMath()的 class ,它需要 2 个值ab Then there is a function named add() which sums up these a and b .然后是一个名为add()的 function ,它总结了这些ab Now i want to record the calculation time.现在我想记录计算时间。 I decided to use decorator here without touching the original function.我决定在这里使用装饰器而不触及原来的 function。 At the same time, i don't want to write this decorator in the same class, So i have created a new class as Handler , and created an instance method as timeit .同时,我不想在同一个 class 中编写这个装饰器,所以我创建了一个新的 class 作为Handler ,并创建了一个实例方法作为timeit But when i am trying to use this decorator as @Handler.timeit , it throws an error TypeError: timeit() missing 1 required positional argument: 'func'但是当我尝试将此装饰器用作@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)

At least from the error i could see there is some issue with @Handlers.timeit至少从错误中我可以看到@Handlers.timeit存在一些问题

Could someone please help me to understand the issue?有人可以帮我理解这个问题吗?

Even if i remove self from decorator as def timeit(func): it is throwing error: TypeError: add() missing 1 required positional argument: 'self'即使我将 self 从装饰器中删除为def timeit(func):它会抛出错误: TypeError: add() missing 1 required positional argument: 'self'

You can use either @classmethod or @staticmethod in this case.在这种情况下,您可以使用@classmethod@staticmethod Here's how I'd approach it:这是我的处理方法:

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)

Out:出去:

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

Update: the *args, **kwargs* is a generalized approach to handle a function that takes any number of parameters, however if you explicitly just to want to support the RMath.add() method - a function which takes a single parameter self - you can use a slightly different approach as below:更新: *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

There are many smaller things that needs to be fixed to make this work.有许多较小的事情需要修复才能完成这项工作。 You have to return the inner function from your decorator, not the result of calling the function.您必须从装饰器返回内部 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. 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.

And since you never create a Handler instance, you should define the wrapping function as a static method (it'll still work even if you don't do this, but there will be no self ).而且由于您从未创建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)

As it's written now, you need a Handlers instance to run Handlers.timeit() , so you need to instantiate the class and use the timeit() method of the instance to decorate the methods in your other classes.正如现在所写,您需要一个Handlers实例来运行Handlers.timeit() ,因此您需要实例化 class 并使用该实例的timeit()方法来装饰其他类中的方法。

But I think what you're wanting to do is use the class as a namespace to hold methods.但我认为您想要做的是使用 class 作为命名空间来保存方法。 In this case you can decorate the methods with @staticmethod .在这种情况下,您可以使用@staticmethod装饰方法。 After all, the method doesn't actually use an instance of the class.毕竟,该方法实际上并未使用 class 的实例。

class Handlers():

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

Or you could just use it as a plain function outside of any class, or (probably best idea) put it in a separate module that you import.或者您可以将它用作任何 class 之外的普通 function,或者(可能是最好的主意)将它放在您导入的单独模块中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM