I want to modify the behaviour of _getattribute_ in "Parent class" (the reason is that a number of attributes are inside an XML element and for other reasons I don't want to dump all the information from the XML element to the class as attributes). The approach I have tried is to define an attribute in the class that collects the name of the attributes that must be searched inside the XML element (only in these ones the behaviour of _getattribute_ is altered).
Important! I'm going to write a simple example because the problem is only in _getattribute_ method and not in the XML management section.
class Parent:
def __init__(self):
self.parentAttr = "some string"
@property
def specialAttrs(self):
return {"parentAttr"}
def __getattribute__(self, attr: str):
if attr in self.specialAttrs:
return "SPECIAL! " self.__dict__[attr]
else:
return super().__getattribute__(attr)
foo = Parent()
print(foo.parentAttr)
This error is raised
RecursionError Traceback (most recent call last)
Cell In[12], line 20
17 return super().__getattribute__(attr)
19 foo = Parent()
---> 20 print(foo.parentAttr)
Cell In[12], line 13, in Parent.__getattribute__(self, attr)
11 def __getattribute__(self, attr: str):
---> 13 if attr in self.specialAttrs:
14 return "SPECIAL! " self.__dict__[attr]
16 else:
Cell In[12], line 13, in Parent.__getattribute__(self, attr)
11 def __getattribute__(self, attr: str):
---> 13 if attr in self.specialAttrs:
14 return "SPECIAL! " self.__dict__[attr]
16 else:
[... skipping similar frames: Parent.__getattribute__ at line 13 (2970 times)]
Cell In[12], line 13, in Parent.__getattribute__(self, attr)
11 def __getattribute__(self, attr: str):
---> 13 if attr in self.specialAttrs:
14 return "SPECIAL! " self.__dict__[attr]
16 else:
RecursionError: maximum recursion depth exceeded
Obviously, the class tries to look for specialAttrs attribute. This originate a recursive search for specialAttrs.
A solution could be change self.specialAttrs by {"parentAttr"}
class Parent:
def __init__(self):
self.parentAttr = "some string"
def __getattribute__(self, attr: str):
if attr in {"parentAttr"}:
return "SPECIAL! " self.__dict__[attr]
else:
return super().__getattribute__(attr)
foo = Parent()
print(foo.parentAttr)
Prints:
SPECIAL! some string
But I need the ability to modify specialAttrs because other classes inherit specialAttrs and extend it.
class Son(Parent):
def __init__(self):
self.sonAttr = "other string"
super().__init__()
@property
def specialAttrs(self):
return super().specialAttrs.union({"sonAttr"})
I am a little bit stacked here. Any suggestion? Thanks!
EDIT 1 (Pranav Hosangadi suggestion)
This is the solution I proposed earlier, a little bit extended.
class Parent:
def __init__(self):
self.parentAttr = "some string"
def __getattribute__(self, attr: str):
if attr in {"parentAttr"}:
return "SPECIAL! " self.__dict__[attr]
else:
return super().__getattribute__(attr)
class Son(Parent):
def __init__(self):
self.sonAttr = "other string"
super().__init__()
def __getattribute__(self, attr: str):
if attr in {"sonAttr"}:
return "SPECIAL! " self.__dict__[attr]
else:
return super().__getattribute__(attr)
foo = Son()
print(foo.parentAttr)
Prints:
SPECIAL! some string
It works.. BUT! I have to redefine the _getattribute_ method in each class that inherits from Parent. I would like to avoid this approach.
CodePudding user response:
The idea is that you don't want any calls to Parent.__getattribute__
inside the Parent.__getattribute__
definition. As you have found out, doing attr in self.specialAttrs
involves exactly this call: self.specialAttrs
calls self.__getattribute__('specialAttrs')
To fix this, change that call to a super().__getattribute__('specialAttrs')
like so:
class Parent:
def __init__(self):
self.parentAttr = "some string"
self.regAttr = 3.14
@property
def specialAttrs(self):
return {"parentAttr"}
def __getattribute__(self, attr: str):
if attr in super().__getattribute__('specialAttrs'):
return "SPECIAL! " super().__getattribute__(attr)
else:
return super().__getattribute__(attr)
Now, it works as expected:
foo = Parent()
print(foo.regAttr) # 3.14
print(foo.parentAttr) # SPECIAL! some string
And you can extend Parent
without having to redefine __getattribute__
:
class Son(Parent):
def __init__(self):
super().__init__()
self.sonAttr = "other string"
self.regAttr = 6.28
@property
def specialAttrs(self):
return super().specialAttrs.union({"sonAttr"})
bar = Son()
print(bar.regAttr) # 6.28
print(bar.parentAttr) # SPECIAL! some string
print(bar.sonAttr) # SPECIAL! other string