Search code examples
pythonpython-internals

Python "__setattr__" and "__getattribute__" confusion


What is wrong with this code?

class Spam(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    # using this to mark field "c" as deprecated. As per my understanding this gets called only for fields that do not exist.
    def __getattr__(self, c):
        print("Deprecated")

    # using this to manipulate the value before storing
    def __setattr__(self, name, value):
        self.__dict__[name] = value + 1

    # interceptor to allows me to define rules for whenever an attribute's value is accessed
    def __getattribute__(self, name):
        return self.__dict__[name] 

spam = Spam(10, 20)

print(spam.a)
print(spam.b)
print(spam.c)

But the above code doesn't print anything. Whats wrong here, can anyone help me understand this? I read about these methods in https://rszalski.github.io/magicmethods/#access


Solution

  • But the above code doesn't print anything

    Wrong. It crashes with infinite recursion.

    In __getattribute__, when you want to log/intercept the call, at some point you still want to get the original method to get the attribute. and self.__dict__[name] calls __getattribute__ so it's not the proper way of doing it.

    What you're trying calls this method again and you get infinite recursion. Call the parent/base method instead:

    # interceptor to allows me to define rules for whenever an attribute's value is accessed
    def __getattribute__(self, name):
        return object.__getattribute__(self,name)  # or super(Spam,self).__getattribute__(name)
    

    that prints:

    11
    21
    Deprecated
    None
    

    None is returned by __getattr__ (because it just prints to the console and implicitly returns None). Maybe an exception would be a better idea.