"Extracting" a dictionary

Peter Otten __peter__ at web.de
Thu May 20 12:52:30 EDT 2004


Noldoaran wrote:

> Arnold Filip:
>> How about this:
>> 
>> In [1]: d = {'foo' : 23, 'bar' : 42}
>> 
>> In [2]: for item in d.items():
>>     ...:         exec "%s = %d" % item
>>     ...:
>> 
>> In [3]: foo
>> Out[3]: 23
>> 
>> In [4]: bar
>> Out[4]: 42
> 
> you could extract them in to there own namespace:
>>>> class AttrDict:
> ...     def __init__(self, d):
> ...         self.__d = d
> ...     def __getattr__(self,attr):
> ...         return self.__d[attr]
> ...     def __setattr__(self,attr,value):
> ...         self.__d[attr] = value
> ...     def __delattr__(self,attr):
> ...         del self.__d[attr]
> ...
>>>> d = AttrDict({'foo' : 23, 'bar' : 42})
> Traceback (most recent call last):
>   File "<input>", line 1, in ?
>   File "<input>", line 3, in __init__
>   File "<input>", line 10, in __setattr__
> (snip)
> RuntimeError: maximum recursion depth exceeded
>>>> d.foo
> 
> That didn't work like I hoped. I decided to share it anyway in case
> someone can get it to work.

Saying self.__d = dont_care invokes __setattr__() to set __d, __setattr__()
asks __getattr__() for __d, which asks __getattr__() for __d to determine
__d ...ad infinitum.
To avoid the recursion you must bypass __setattr__() by accessing __dict__
directly (and do some name mangling due to the evil double-underscore
attribute name by hand):

>>> class AttrDict:
...     def __init__(self, d):
...             self.__dict__["_AttrDict__d"] = d
...     def __getattr__(self, attr):
...             return self.__d[attr]
...     def __setattr__(self, attr, value):
...             self.__d[attr] = value
...     def __delattr__(self, attr):
...             del self.__d[attr]
...
>>> d = AttrDict({'foo': 23, 'bar': 42})
>>> d.foo
23
>>> d.baz = 123
>>> d.foo = 456
>>> d.__d
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 5, in __getattr__
KeyError: '__d'
>>> d.AttrDict__d
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 5, in __getattr__
KeyError: 'AttrDict__d'
>>> d._AttrDict__d
{'baz': 123, 'foo': 456, 'bar': 42}
>>>

While this works, you can achieve the same functionality in a more elegant
way:

>>> class AttrDict:
...     def __init__(self, d):
...             self.__dict__.update(d)
...
>>> d = AttrDict({"foo": 23, "bar": 42})
>>> d.bar
42
>>> d.foo *= 2
>>> d.__dict__
{'foo': 46, 'bar': 42}
>>> del d.foo
>>> d.__dict__
{'bar': 42}
>>>

A standard trick, by the way. Can't think of the right google keywords right
now, so I use another standard trick and leave finding them as an excercise
to the reader :-)

Peter




More information about the Python-list mailing list