which "dictionary with attribute-style access"?

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Mon Oct 12 23:57:20 CEST 2009


En Mon, 12 Oct 2009 16:58:35 -0300, Andreas Balogh <baloand at gmail.com>  
escribió:

>  googling I found several ways of implementing a "dictionary with  
> attribute-style access".
>  1. ActiveState cookbook: http://code.activestate.com/recipes/473786/
>  2. ActiveState cookbook:  
> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/361668
>  3. web2py codebase: Storage(dict)
>  I enclosed the three implementations below.
>  My question to the Python specialists: which one is the most correct?
> Are there restrictions with regards to pickling or copy()?
> Which one should I choose?

> -----------------------------------------------------------------------
> class AttrDict(dict):
>     """ comments removed """
>     """A dictionary with attribute-style access. It maps attribute  
> access to the real dictionary.  """
> 	def __init__(self, init={}):
> 		dict.__init__(self, init)
>  	def __getstate__(self):
> 		return self.__dict__.items()
>	def __setstate__(self, items):
> 		for key, val in items:
> 			self.__dict__[key] = val
>  	def __repr__(self):
> 		return "%s(%s)" % (self.__class__.__name__, dict.__repr__(self))
>  	def __setitem__(self, key, value):
> 		return super(AttrDict, self).__setitem__(key, value)
>  	def __getitem__(self, name):
> 		return super(AttrDict, self).__getitem__(name)
>  	def __delitem__(self, name):
> 		return super(AttrDict, self).__delitem__(name)
>  	__getattr__ = __getitem__
> 	__setattr__ = __setitem__
>  	def copy(self):
> 		ch = AttrDict(self)
> 		return ch

__init__, __setitem__, __getitem__, __delitem__ are redundant if they just  
call the inherited behaviour.
__getstate__/setstate are redundant too, because the base class (dict)  
already knows how to serialize itself, and AttrDict is not adding  
additional attributes. The remaining methods are __repr__, __getattr__,  
__setattr__ (why not __delattr__?) and copy, but still don't work very  
well (e.g. __getattr__ should raise AttributeError, not KeyError, for  
unknown attributes).

> -----------------------------------------------------------------------
> class attrdict(dict):
>     """ comments removed """
>     def __init__(self, *args, **kwargs):
>         dict.__init__(self, *args, **kwargs)
>         self.__dict__ = self
>

I like this -- I'd add repr/copy/fromkeys so they are aware of the  
subclassing.

> -----------------------------------------------------------------------
> class Storage(dict):
>     """ comments removed """
>     def __getattr__(self, key):
>         try:
>             return self[key]
>         except KeyError, k:
>             return None
>      def __setattr__(self, key, value):
>         self[key] = value
>      def __delattr__(self, key):
>         try:
>             del self[key]
>         except KeyError, k:
>             raise AttributeError, k
>      def __repr__(self):
>         return '<Storage ' + dict.__repr__(self) + '>'
>      def __getstate__(self):
>         return dict(self)
>      def __setstate__(self, value):
>         for (k, v) in value.items():
>             self[k] = v

Not so bad, but some comments above still apply.
Try to build the best implementation from all those pieces. Below are some  
tests I'd expect a decent implementation should pass:

assert Subclass() == {}
d1 = {1:2, '3':4, 'name': 'value', '__getattr__': 5, '__getitem__': 6}
d2 = Subclass(d1)
assert d1 == d2

assert d2.copy() == d2
assert isinstance(d2.copy(), type(d2))
assert eval(repr(d2)) == d2
assert isinstance(eval(repr(d2)), type(d2))

d3 = Subclass.fromkeys([1,2,3])
assert isinstance(d3, Subclass)
assert d3 == {1:None, 2:None, 3:None}

assert d2[1] == 2
assert d2['name'] == d2.name == 'value'
assert d2.__getattr__ == 5

assert not hasattr(d2, 'xyz')
assert 'xyz' not in d2
d2.xyz = 123
assert d2.xyz == d2['xyz'] == 123
assert 'xyz' in d2

d2['xyz'] = 456
assert d2['xyz'] == d2.xyz == 456
assert hasattr(d2, 'xyz')

d2['abc'] = 789
assert d2.abc == d2['abc'] == 789

d2.abc = 123
assert d2.abc == d2['abc'] == 123

del d2.abc
assert not hasattr(d2, 'abc')
assert 'abc' not in d2
del d2['xyz']
assert not hasattr(d2, 'xyz')
assert 'xyz' not in d2

d4 = loads(dumps(d2))
assert d2 == d4
assert isinstance(d4, type(d2))

(plus all the expected behavior from dict itself: clear, get, update...)

-- 
Gabriel Genellina




More information about the Python-list mailing list