How to use __setattr__ correctly, avoiding infinite recursion
I want to define a class containing read
and write
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 is device
to redirect the return to self
. 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 attribute x
, it calls __setattr__
, which again calls __setattr__
and so on. How do I need to change this code, that, in this case, a new attribute x
of self
is created, holding the value 1
?
Or is there any better way to handle calls like instance.device.read
to be 'mapped' to instance.read
?
As there are always questions about the why: I need to create abstractions of xmlrpc
calls, for which very easy methods like myxmlrpc.instance,device.read
and similar can be created. I need to 'mock' this up to mimic such multi-dot-method calls.
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