Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python make dictionary items accessible as object property

Tags:

python

This is mostly syntactic sugar but I'd like to access the items of a dictionary as object properties.

Example:

class CoolThing():
  def __init__(self):
    self.CoolDict = {'a': 1, 'b': 2}

and I'd like to have

my_cool_thing.a # => 1
my_cool_thing.b # => 2

Edit: some code of a potential solution with a nested structure with dot notation: device.property.field

class Parameters():

    def __init__(self, ids, devices):
        self._ids = ids
        self._devices = devices
        for p in self._devices:
            p = p[0]
            if self.__dict__.get(p.device) is None:
                self.__dict__[p.device] = SmartDict()
            else:
                if self.__dict__[p.device].get(p.property) is None:
                    self.__dict__[p.device][p.property] = SmartDict()
                else:
                    if self.__dict__[p.device][p.property].get(p.field) is None:
                        self.__dict__[p.device][p.property][p.field] = ParameterData(p)

class SmartDict():
    def __init__(self):
        self.__dict__ = {}

    def __getitem__(self, k):
        return self.__dict__[k]

    def __setitem__(self, k, v):
        self.__dict__[k] = v

    def get(self, k):
        return self.__dict__.get(k)

    def __len__(self):
        return len(self.__dict__)
like image 339
Cedric H. Avatar asked Dec 18 '22 20:12

Cedric H.


1 Answers

You want __getattr__ and __setattr__, though you'll have to roll your own class (I'm not aware of any builtins, though namedtuple might work if you don't need to change values much)

class AttrDict(dict):
    def __getattr__(self, attr):
        return self[attr]

    def __setattr__(self, attr, value):
        self[attr] = value

If you just want to access a sub-dictionary that way, you just change self to self.cool_dict

class CoolThing:
   def __init__(self):
      self.cool_dict = {'a': 1, 'b': 2}

   def __getattr__(self, attr):
      return self.cool_dict[attr] 

   def __setattr__(self, attr, value):
      # Note, you'll have to do this for anything that you want to set
      # in __init__. 
      if attr == 'cool_dict':
          super().__setattr__(attr, value)
      else:
          self.cool_dict[attr] = value

Note that __getattr__ is used after any other lookups fail, but if you want to ensure that your function is called first, you can use __getattribute__

Also note that self.cool_dict does not exist on CoolThing until after __init__ is called. My initial version of this would throw a maximum recursion depth exceeded, because as you created the class it would go to set self.cool_dict in init, call __setattr__, which would try to get self.cool_dict so it could set [attr] = value on it. Naturally it can't find cool_dict yet, and so it will try to call __getattr__ again... which can't find cool_dict and round and round it goes.

Another option would be to use a class-level variable instead, but that's probably not at all what you want :)

like image 188
Wayne Werner Avatar answered Dec 21 '22 09:12

Wayne Werner