2

I want to use a decorator to do some preparation job and record the status the function have, so I write something like that:

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) 

But it's obvious that self in __call__(self, *args, **kwargs) corresponds to instance of Decorator instead of the instance of Foo , which will make f.value unchanged but f.test.value increase .

Is there any way I can pass the instance of Foo to Decorator instead of Decorator itself?

Or is there any way to implement this function much more clear?

0

3 Answers 3

4

As the decorator is only called once and replaces the method for all instance with one instance of the Decorator class. All it does is:

Foo.test = Decorator(Foo.test) 

This makes it impossible to detect the instance called. One work-around would be to apply the decorator in the __init__ of Foo by hand:

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) 

This way the decorator wraps the instance method, so you do not need to pass self in the __call__ of 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) 

Now it works and you have to update you test method, as f.test.value no longer exists:

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

It outputs two times a 1 as expected.

Sign up to request clarification or add additional context in comments.

Comments

1

I got this 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 

or May be this

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 

Comments

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

I found using Priyesh Kumar's answer that you can simply pass the self argument from the call method to the function being decorated:

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

hope this helps!

EDIT: Never mind only works if the function being passed through the decorator does not call class variables defined in the init method

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.