Reputation: 831
I have a class devices
that inherits from the dict class. devices is a dictionary of dictionaries. I would like to access its keys and values by using a method call syntax, and as well as using the normal syntax. i.e.
class Devices(dict):
def __init__(self):
self['device'] = {'name':'device_name'}
def __getattr__(self, name):
value = self[name]
return value
...
mydevices = Devices()
mydevice = mydevices.device #works and mydevices['device'] also works
#But what code do I need for the following?
name = mydevices.device.name
mydevices.device.name = 'another_name'
I know that if I override the __getattr__
and __setattr__
methods I can achieve this, but as you can see from my code I am not sure how to access a nested dictionary.
Does anyone know how to achieve this?
thanks, labjunky
Upvotes: 3
Views: 3585
Reputation: 64318
You can use this tricky class, which is a subclass of dict
, and whose keys can be accssed either as items (d[k]
) or as attributes (d.k
):
class Struct(dict):
def __init__(self,*args, **kwargs):
dict.__init__(self,*args, **kwargs)
self.__dict__ = self # yes, it is tricky
The key here that the interpreter would check the __dict__
before calling __[gs]etattr__
, so you don't even need to override them.
Now you can do:
class Devices(Struct):
pass
devices = Devices()
devices.device = Struct(name='devicename')
devices.device.name = 'anothername'
Upvotes: 1
Reputation: 2361
In general, to access a dictionary values use dictionary[key]
and not dictionary.key
. In the first case the attribute way works because you overrode __getattr__
instead of __getitem__
. That's why it works only for you class, but not for the inner dictionary.
Now, to access the dictionary in __getitem__
(__getattr__
is for attribute, like dict.keys
where the word keys
is the attribute) you need to be a bit careful. You need to call the __getitem__
of the dictionary class (otherwise you'll have infinite recursion). There are two ways to achieve it. The first one will be persistent if you choose to change you object hereditary:
def __getitem__(self, name):
value = super(Devices,self).__getitem__(name)
return value
Another way is:
def __getitem__(self, name):
value = dict.__getitem__(self, name)
return value
If you still want to use attributes to access the values you'll have to replace the dictionary with your class whenever you use it (so self['device'] = {'name':'device_name'}
will become self['device'] = MyDictClass({'name':'device_name'})
.
BTW: Note that you have an error in the __init__
. The line self = {'device':{'name':'device_name'}}
in __init__
actually doesn't do anything. (It puts the dictionary in the local variable self
, but this variable disappears as soon as you exit the function.)
You should use operation that change the object itself instead. I.e.:
def __init__(self):
self['device'] = {'name':'device_name'}
Upvotes: 1
Reputation: 7842
So your answer is pretty much complete anyway. You can define the kind of structure you want with:
class Devices(dict):
def __init__(self,inpt={}):
super(Devices,self).__init__(inpt)
def __getattr__(self, name):
return self.__getitem__(name)
def __setattr__(self,name,value):
self.__setitem__(name,value)
Then a use case would be:
In [6]: x = Devices()
In [7]: x.devices = Devices({"name":"thom"})
In [8]: x.devices.name
Out[8]: 'thom'
Basically just nest your attribute-look-up dictionaries.
Upvotes: 2