Reputation: 1220
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?
Upvotes: 2
Views: 1023
Reputation: 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))
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
Upvotes: 0
Reputation: 2857
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
Upvotes: 1
Reputation: 3036
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.
Upvotes: 4