类作为类方法的装饰器

时间:2016-06-13 14:24:18

标签: python decorator

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

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) 

但显而易见的是,self中的__call__(self, *args, **kwargs)对应Decorator的实例,而不是Foo的实例,这将使f.value保持不变,但f.test.value 1}}增加。

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

或者有没有办法更清楚地实现这个功能?

先谢谢。

3 个答案:

答案 0 :(得分:4)

由于装饰器只被调用一次并用一个Decorator类的实例替换所有实例的方法。它所做的就是:

Foo.test = Decorator(Foo.test)

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

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)

这样装饰器会包装实例方法,因此您无需在self的{​​{1}}中传递__call__

Decorator

现在它可以工作,你必须更新测试方法,因为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 :(得分:0)

我得到了这个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

答案 2 :(得分:0)

<section class="hero-area">
    <div class="container">
        <div class="row">
            <div class="col-md-12">
            <div style="margin-top:100px; margin-right:30px; height:506px; width:332px; float: right; background:url(images/Mobile-Screen.png)"  >

<div class="play-backdrop"></div>
<div class="play-button">
  <svg class="play-circles" viewBox="0 0 152 152">
    <circle class="play-circle-01" fill="none" stroke="#fff" stroke-width="3" stroke-dasharray="343 343" cx="76" cy="76" r="72.7"/>
    <circle class="play-circle-02" fill="none" stroke="#fff" stroke-width="3" stroke-dasharray="309 309" cx="76" cy="76" r="65.5"/>
  </svg>
  <div class="play-perspective">
    <button class="play-close"></button>
    <div class="play-triangle">
      <div class="play-video">
        <iframe width="600" height="400" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
      </div>
    </div>
  </div>
</div>     
            </div>

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

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))

希望这会有所帮助!

编辑: 没关系,仅当通过装饰器传递的函数未调用init方法中定义的类变量时才起作用