How to use __setattr__ correctly, avoiding infinite recursion
Solution 1
You must call the parent class __setattr__
method:
class MyTest(object):
def __init__(self, x):
self.x = x
def __setattr__(self, name, value):
if name=="device":
print "device test"
else:
super(MyTest, self).__setattr__(name, value)
# in python3+ you can omit the arguments to super:
#super().__setattr__(name, value)
Regarding the best-practice, since you plan to use this via xml-rpc
I think this is probably better done inside the _dispatch
method.
A quick and dirty way is to simply do:
class My(object):
def __init__(self):
self.device = self
Solution 2
Or you can modify self.__dict__
from inside __setattr__()
:
class SomeClass(object):
def __setattr__(self, name, value):
print(name, value)
self.__dict__[name] = value
def __init__(self, attr1, attr2):
self.attr1 = attr1
self.attr2 = attr2
sc = SomeClass(attr1=1, attr2=2)
sc.attr1 = 3
Solution 3
You can also use object.
class TestClass:
def __init__(self):
self.data = 'data'
def __setattr__(self, name, value):
print("Attempt to edit the attribute %s" %(name))
object.__setattr__(self, name, value)
Solution 4
or you can just use @property:
class MyTest(object):
def __init__(self, x):
self.x = x
@property
def device(self):
return self
Solution 5
If you don't want to specify which attributes can or cannot be set, you can split the class to delay the get/set hooks until after initialization:
class MyTest(object):
def __init__(self, x):
self.x = x
self.__class__ = _MyTestWithHooks
class _MyTestWithHooks(MyTest):
def __setattr__(self, name, value):
...
def __getattr__(self, name):
...
if __name__ == '__main__':
a = MyTest(12)
...
As noted in the code you'll want to instantiate MyTest, since instantiating _MyTestWithHooks will result in the same infinite recursion problem as before.
Alex
Updated on July 05, 2022Comments
-
Alex almost 2 years
I want to define a class containing
read
andwrite
methods, which can be called as follows:instance.read instance.write instance.device.read instance.device.write
To not use interlaced classes, my idea was to overwrite the
__getattr__
and__setattr__
methods and to check, if the given name isdevice
to redirect the return toself
. But I encountered a problem giving infinite recursions. The example code is as follows:class MyTest(object): def __init__(self, x): self.x = x def __setattr__(self, name, value): if name=="device": print "device test" else: setattr(self, name, value) test = MyTest(1)
As in
__init__
the code tried to create a new attributex
, it calls__setattr__
, which again calls__setattr__
and so on. How do I need to change this code, that, in this case, a new attributex
ofself
is created, holding the value1
?Or is there any better way to handle calls like
instance.device.read
to be 'mapped' toinstance.read
?As there are always questions about the why: I need to create abstractions of
xmlrpc
calls, for which very easy methods likemyxmlrpc.instance,device.read
and similar can be created. I need to 'mock' this up to mimic such multi-dot-method calls. -
Alex about 11 yearsI like the quick-and-dirty way: Just one line to get the desired behaviour! Thanks
-
djvg over 5 yearsThis is also mentioned in the Python docs.
-
Maxpm about 5 yearsWhy does this not call
setattr(self, "__dict__", value)
, repeating the original infinite recursion problem? -
Se Norm about 5 yearsBecause this is not setting the
__dict__
attribute, gut getting it and then setting an item on the returned dict, i.e., it's calling sth like:getattr(self, "__dict__").__setitem__(name, value)
-
Admin almost 5 yearsThis a builtin class. All classes inherits from this class (even
__setattr__
,__getattr__
,__dict__
, ...) -
thomas.mac almost 5 yearssimilar to type then ?
-
Admin almost 5 yearsIt's not fully similar,
type(TestClass) == object
ortype == object
returns False. Note that everything has the __setattr__ method even a string:"a".__setattr__
because all classes inherits from the builtin classobject
even strings -
Shahryar about 4 years@Bakuriu would you please explain what this line is doing exactly? super(MyTest, self).__setattr__(name, value)
-
Bakuriu about 4 years@Shahryar It is explained in the first line of my answer. It is calling the parent class
__setattr__
method.super(Class, instance)
creates an object that is able to call the methods of the parent class ofClass
oninstance
. You could also doobject.__setattr__(instance, name, value)
in this case butsuper
is able to handle correctly multi-inheritance and should be preferred. -
Shahryar about 4 yearswhy can't we simply say self[name] = value? is it wrong?
-
Bakuriu about 4 years@Shahryar Because that's not even valid python syntax.
self[name] = value
is equivalent toself.__setitem__(name, value)
which most of the time is not implemented. And if you try to use thesetattr
builtin what would happen is thatsetattr(self, name value)
will recursively call__setattr__
and you end up in an infinite loop.