【问题标题】:Class as decorator for class method类作为类方法的装饰器
【发布时间】:2016-10-14 00:28:39
【问题描述】:

我想用装饰器做一些准备工作并记录函数的状态,所以我写了这样的东西:

class Decorator:
    def __init__(self, func):
        self.count = 0
        self.func = func

    def __call__(self, *args, **kwargs):
        self.count += 1 # Simply count the call times
        return self.func(self, *args, **kwargs)

class Foo:
    def __init__(self):
        self.value = 0

    @Decorator
    def test(self, value):
        self.value = value # change the value of instance
        print(self.value)

f = Foo()
f.test(1)

print(f.value)
print(f.test.value) 

但很明显__call__(self, *args, **kwargs)中的self对应的是Decorator的实例,而不是Foo的实例,这会使f.value不变,但f.test.value增加。

有什么方法可以将Foo 的实例传递给Decorator 而不是Decorator 本身?

或者有什么方法可以更清楚地实现这个功能?

非常感谢。

【问题讨论】:

  • f.test.value 是做什么的?
  • 我 200% 确定我没有完全理解您的问题。就像在您的代码中一样,您想记录一个带有 @Decorator 的方法被调用了多少次。这很好,但稍后您将如何访问该 count。我尝试了一个示例,您可以将 Foo 的实例传递给 Decorator,但 f.test.value 不起作用
  • 您可以使用f.test.count 访问计数,其中fFoo 的一个实例。

标签: python decorator


【解决方案1】:
class threadSafeGenerator(object):
    """docstring for threadSafeGenerator"""
    class SafeGenerator(object):
        """docstring for SafeGenerator"""
        def __init__(self, iterable):
            self.iterable = iterable
            self.lock = Lock()
        def __iter__(self):
            return self

        def __next__(self):
            with self.lock:
                return next(self.iterable)

    def __init__(self, func):
        super(threadSafeGenerator, self).__init__()
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.SafeGenerator(self.func(self, *args, **kwargs))

我发现使用 Priyesh Kumar 的回答,您可以简单地将 call 方法中的 self 参数传递给被修饰的函数:

def __call__(self, *args, **kwargs):
    return self.SafeGenerator(self.func(self, *args, **kwargs))

希望这会有所帮助!

编辑: 没关系,只有在通过装饰器传递的函数不调用 init 方法中定义的类变量时才有效

【讨论】:

    【解决方案2】:

    我收到了这个here

    import functools
    
    class Decorator(object):
        def __init__(self, func):
            self.count = 0
            self.func = func
    
    
        def __call__(self, *args, **kwargs):
            self.count += 1 # Simply count the call times
            return self.func( *args, **kwargs)
    
        def __get__(self, instance, instancetype):
            """Implement the descriptor protocol to make decorating instance 
            method possible.
            """
    
            # Return a partial function with the first argument is the instance 
            #   of the class decorated.
            return functools.partial(self.__call__, instance)
    
    
    
    class Foo:
        def __init__(self):
            self.value = 0
    
        @Decorator
        def test(self, value):
            self.value = value # change the value of instance
    
    
    
    f = Foo()
    f.test(3)
    print(f.value)  # prints 3
    
    
    g = Foo()
    g.test(8)
    print(g.value) # prints 8
    

    可能是这个

    def preJob(function):
        def updateToDo(self, *args, **kwargs):
            # do some recording
            function(self, *args, **kwargs)
        return updateToDo
    
    class Foo(object):
        def __init__(self):
            self.value = 0
    
        @preJob
        def test(self, value):
            self.value = value
    
    f = Foo()
    f.test(3)
    print(f.value)  # prints 3
    
    
    g = Foo()
    g.test(8)
    print(g.value) # prints 8
    

    【讨论】:

      【解决方案3】:

      因为装饰器只被调用一次,并且用装饰器类的一个实例替换所有实例的方法。它所做的只是:

      Foo.test = Decorator(Foo.test)
      

      这使得无法检测到调用的实例。一种解决方法是手动在Foo__init__ 中应用装饰器:

      class Foo:
          def __init__(self):
              self.value = 0
              self.test = Decorator(self.test)
      
          def test(self, value):
              self.value = value # change the value of instance
              print(self.value)
      

      这样装饰器包装了实例方法,所以你不需要在Decorator__call__中传递self

      class Decorator:
          def __init__(self, func):
              self.count = 0
              self.func = func
      
          def __call__(self, *args, **kwargs):
              self.count += 1 # Simply count the call times
              return self.func(*args, **kwargs)
      

      现在它可以工作了,你必须更新你的测试方法,因为f.test.value 不再存在:

      f = Foo()
      f.test(1)
      
      print(f.value)
      

      它按预期输出两次1

      【讨论】:

        猜你喜欢
        • 2014-02-18
        • 2014-01-14
        • 2021-04-12
        • 1970-01-01
        • 2020-01-11
        • 1970-01-01
        • 2022-01-06
        • 2018-08-15
        • 2019-02-20
        相关资源
        最近更新 更多