Search code examples
pythonsetattr

How to use __setitem__ properly?


I want to make a data object:

class GameData:
  def __init__(self, data={}):
    self.data = data

  def __getitem__(self, item):
    return self.data[item]

  def __setitem__(self, key, value):
    self.data[key] = value

  def __getattr__(self, item):
    return self.data[item]

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

  def __repr__(self):
    return str(self.data)

When I create a GameData object, I get RecursionError. How can I avoid setitem recall itself?


Solution

  • In the assignment self.data = data, __setattr__ is called because self has no attribute called data at the moment. __setattr__ then calls __getattr__ to obtain the non-existing attribute data. __getattr__ itself calls __getattr__ again. This is a recursion.

    Use object.__setattr__(self, 'data', data) to do the assignment when implementing __setattr__.

    class GameData:
      def __init__(self, data=None):
        object.__setattr__(self, 'data', {} if data is None else data)
    
      def __getitem__(self, item):
        return self.data[item]
    
      def __setitem__(self, key, value):
        self.data[key] = value
    
      def __getattr__(self, item):
        return self.data[item]
    
      def __setattr__(self, key, value):
        self.data[key] = value
    
      def __repr__(self):
        return str(self.data)
    

    For details, see the __getattr__ manual

    Additionally, do not use mutable objects as default parameter because the same object {} in the default argument is shared between GameData instances.