How can apply a decorator to __setattr__
method? I've tried this way but print
inside of wrapper doesn't work. (I know we can change setattr via inheritance, but need to do it via decorator / other way). Thanks in advance.
from functools import wraps
class A():
pass
x = A()
x.a = 2
def setattr_decorator(func):
@wraps(func)
def wrapper(self, name: str, value):
print('called')
object.__setattr__(self, 'custom_' name, value)
return wrapper
x.__setattr__ = setattr_decorator(x.__setattr__)
x.b = 2
x.custom_b # invokes AttributeError
CodePudding user response:
Define the __setattr__
within the class & apply the decorator there. You also need to actually call the wrapped function within the wrapper:
from functools import wraps
def setattr_decorator(func):
@wraps(func)
def wrapper(self, name: str, value):
print('called')
func(self, name, value)
object.__setattr__(self, 'custom_' name, value)
return wrapper
class A():
@setattr_decorator
def __setattr__(self, key, value):
object.__setattr__(self, key, value) # or pass if you don't want to set the 'a' or 'b' attribute
x = A()
x.a = 1
x.b = 2
print(x.custom_a)
print(x.custom_b)
Result:
called
called
1
2
Edit:
In case you can't change the class A
itself, you can apply the decorator on the class method manually:
A.__setattr__ = setattr_decorator(A.__setattr__)
Which should work in the same way. Note: the decorator needs to be applied on the class, not the object.
CodePudding user response:
By turning the decorator into a function you can achieve the same goal and avoid redundant syntax.
from functools import wraps
def load_setattr(target_cls, prefix='custom_'):
# modify inplace of the target class
def wrapper(self, name: str, value):
print('called')
object.__setattr__(self, prefix name, value)
target_cls.__setattr__ = wraps(target_cls.__setattr__)(wrapper)
class A:
pass
load_setattr(A)
x = A()
x.a = 1
#called
x.b = 2
#called
print(x.custom_a)
#1
print(x.custom_b)
#2