将装饰器定义为类内的方法

时间:2018-04-23 05:14:04

标签: python python-decorators

我正在尝试在我的类中创建一个计算特定函数的完整运行的方法。我想使用一个简单的装饰器。我发现了这个reference并重写了这个简单的脚本:

class myclass:
    def __init__(self):
        self.cnt = 0

    def counter(function):
        """
        this method counts the number of runtime of a function
        """
        def wrapper(self, **args):
            function(**args)
            self.counter += 1
        return wrapper


@myclass.counter
def somefunc():
    print("hello from somefunc")


if __name__ == "__main__":
    obj = myclass()
    # or if comment @myclass.counter
    # somefunc = myclass.counter(somefunc)
    somefunc()

我当然得到:

TypeError: wrapper() missing 1 required positional argument: 'self'

我尝试将计数器重写为类方法:

class myclass:
    def __init__(self):
        self.cnt = 0

    def counter(self, function):
        """
        this function counts the number of runtime of a function
        """
        def wrapper(**args):
            function(**args)
            self.cnt += 1
        return wrapper


def somefunc():
    print("hello from somefunc")


if __name__ == "__main__":
    obj = myclass()
    somefunc = obj.counter(somefunc)
    for i in range(10):
        somefunc()
        print(obj.cnt)

哪个工作正常,但我认为它不是一个有效的装饰器定义。有没有办法在类方法中定义装饰器并将自身参数传递给它的函数?或者在类中定义装饰器是没用的?

编辑:------ 首先,我不能在类方法之外定义装饰。其次,我正在尝试创建一个运行特定函数(作为输入)的预定类,用于固定的时间间隔和特定的时间,因此我需要对其进行计数。

2 个答案:

答案 0 :(得分:2)

所以我能够为你起草一些东西,下面是代码:

def count(func):
    def wrapper(self):
        TestClass.call_count += 1
        func(self)

    return wrapper


class TestClass(object):
    call_count = 0

    @count
    def hello(self):
        return 'hello'


if __name__ == '__main__':
    x = TestClass()
    for i in range(10):
        x.hello()

    print(TestClass.call_count)

为什么在类中设置装饰器会导致问题:

在课堂上有一个decorator function并不是直截了当的。原因如下:

原因1 每个类方法都必须使用参数self,它是调用函数的类的instance。现在,如果您使装饰器函数采用self参数,装饰器调用@count将失败,因为它转换为count(),但不传递self参数,因此错误:

  

TypeError:wrapper()缺少1个必需的位置参数:'self'

原因2 现在,为了避免这种情况,您可以通过更改以下声明来使decorator成为静态:

@staticmethod
def count(func):
    pass

但是你又有另一个错误:

  

TypeError:'staticmethod'对象不可调用

这意味着你也不能拥有静态方法。如果在类中没有静态方法,则必须将self实例传递给方法,但如果将self实例传递给它,则@count装饰器调用将不会不通过self实例,因此无效。

所以here是一个很好地解释它的博客,与之相关的问题以及有哪些替代方案。

我个人更喜欢选择让helper class来保存我可以使用的所有decorators,而不是它所定义的唯一类。这将使您可以灵活地重用装饰器,而不是重新定义它们,这将遵循意识形态

  

代码一次,重复使用。

答案 1 :(得分:1)

您的第二个代码示例在功能上等同于标准装饰器。标准装饰器语法只是同一个东西的简写,即重新分配一个等于闭包的函数值(一个带有预定义参数的函数指针),其中闭包是装饰器包装器,将原件保存为预定义参数。

这是标准语法的等价物。请注意,您需要提前创建计数器类实例。装饰器语法引用该实例,因为它必须指示保存计数器的特定对象,而不仅仅是对象的类:

class myclass:
    def __init__(self):
        self.cnt = 0

    def counter(self,function):
        """
        this method counts the number of runtime of a function
        """
        def wrapper(**args):
            function(self,**args)
            self.cnt += 1
        return wrapper

global counter_object
counter_object = myclass()

@counter_object.counter
def somefunc(self):
    print("hello from somefunc")

if __name__ == "__main__":
    for i in range(10):
        somefunc()
        print(counter_object.cnt)