Search code examples
pythonpython-2.7setattr

Python, how to use __setattr__ on dictionary object that is part of the class that overloads the method?


As illustrated in the code below, why can't I use __setattr__ to set values on a dict that is part of the class that overloads the method? I expected that b.hello would not exist.

class MyClass():

    datastore = {}

    def __init__(self):
        self.datastore = {}

    def __getattr__(self, key):
        return self.datastore[key]

    def __setattr__(self, key, value):
        self.datastore[key] = value

a = MyClass()
b = MyClass()

a.hello = "err"

print a.hello # err
print b.hello # err

Solution

  • b.hello prints your string "err" because datastore is an attribute of the class itself, not of objects of the class. Therefore, when you initialize it in a, b can also access it.

    Therefore, remove the datastore = {} from the class.

    Furthermore, from the Python docs:

    if __setattr__() wants to assign to an instance attribute, it should not simply execute self.name = value — this would cause a recursive call to itself. Instead, it should insert the value in the dictionary of instance attributes, e.g., self.__dict__[name] = value. For new-style classes, rather than accessing the instance dictionary, it should call the base class method with the same name, for example, object.__setattr__(self, name, value).

    So, change your code to:

    class MyClass(object): # Use new style classes
        def __init__(self):
            object.__setattr__(self, 'datastore', {}) # This prevents infinite recursion when setting attributes
    
        def __getattr__(self, key):
            return self.datastore[key]
    
        def __setattr__(self, key, value):
            self.datastore[key] = value
    
    a = MyClass()
    b = MyClass()
    
    a.hello = "err"
    
    print a.hello # Works
    print b.hello # Gives an error