Python modify __getattribute__

Question:

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.

Asked By: Virtualvikings

||

Answers:

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
Answered By: Pranav Hosangadi