AttrDict
AttrDict copied to clipboard
why does AttrMap return a tuple when a list is stored?
from attrdict import AttrMap as mp
m=mp({"a":[1,2,3]})
m.a
return (1,2,3)
This sould not happen, right? Would be really good to fix this :-) Thanks :+1:
m["a"] by the way returns the list however
AttrDict also return tuple ex) ad = AttrDict({'foo': ['bar']}) assert ad.foo == ('bar') assert ad['foo'] == ['bar']
I started using this in a project, and now I regret it because of this behavior, which is completely fucked.
This is Python. You don't silently change the type of objects passed to a data structure!
Not to mention it's not even consistent.
>>> a = AttrDict()
>>> a.one = [1, 2, 3]
>>> type(a.one)
tuple
>>> type(a['one'])
list
my mistake for assuming something simple like this would have been executed without huge pitfalls.
While I'm not sure what the justification is for this behavior by default, it's very easily worked-around. It takes longer to post a complaint on Github than to look at the method signatures to find out how to change the undesired behavior.
The constructors for AttrMap
and AttrDefault
take a sequence_type
argument, which you just need to set to list
:
>>> m = AttrMap(sequence_type=list)
>>> m.l = [1, 2, 3]
>>> m.l
[1, 2, 3]
>>> type(m.l)
<class 'list'>
Strangely, AttrDict
doesn't take this parameter, but it's possible to change its behavior as well with a hack:
>>> d = AttrDict()
>>> d._setattr('_sequence_type', list)
>>> d.l = [1, 2, 3]
>>> d.l
[1, 2, 3]
>>> type(d.l)
<class 'list'>
Thanks so this might be solved
I have a problem accessing an xarray.Dataset object which, when returned, is converted to an attrdict.dictionary.AttrDict.
I really want to suppress this behaviour.
Its the _build method that changes the types so I have simply patched it like so:
class MyAttrDict(AttrDict):
def _build(self, obj):
return obj
This means that you can only access attributes to the first dot (no foo.bar.hello.world) but that is all I need and want.
jeffkayser's solution is helpful for the special case, but the general problem remains:
If you set the default sequence_type to list, empty tuples are silently converted to lists.
This is a genuine bug.
jeff Kayser's solution does not in fact workl (neither does changing the the default in /attrdict/dictionary.py) append silently fails:
d = AttrDict() d._setattr('_sequence_type', list) d.l = [1, 2, 3] d.l [1, 2, 3] type(d.l) d AttrDict({'l': [1, 2, 3]}) d.l [1, 2, 3] d.l.append('x') d.l [1, 2, 3]
https://github.com/jonathanstrong/attr_dict
Thanks jonathanStrong. Backatcha. It's new, so comments welcome.
#from attrdict import AttrDict class AttrDict(dict): #http://code.activestate.com/recipes/576972-attrdict/; def init(self, *a, **kw): dict.init(self, *a, **kw) self.dict = self
verbose=False class AttrThing(AttrDict): """ AttrThing does what AttrDict does can return multiple elements returns keys() and values() as lists to support indexing does not turn lists into tuples (c.f., https://pypi.python.org/pypi/attrdict') """
def __call__(self, *args, **kwargs):
#print('in __call__', args, kwargs)
for k,v in kwargs.items(): #allow a(this='this', that='that') for existing object
#self[k]=v
self.__setattr__(k,v)
if args:
if len(args)==1: return self.__getitem__(args[0])
ret=[] #but also field multiple requests and return answers as lists
for arg in args:
ret.append(self.__getitem__(arg))
return ret
def keys(self):
return list(super(AttrThing, self).keys())
def values(self):
return list(super(AttrThing, self).values())
if name=='main': a=AttrThing() a(first='Can be initialized this way', second='See?') print(a) print() #{'first': 'Can be initialized this way', 'second': 'See?'}
del a.first, a.second
a['this']='settable with bracket'
print(a)
print()
#{'this': 'settable with bracket'}
#like AttrDict
a.this='settable with dot'
print(a)
print()
#{'this': 'settable with dot'}
#like AttrDict, gettable 3 ways
a.this='THIS'
print(a.this, a['this'], a('this'), '\t\t', a.this==a['this']==a('this'), "\t\t Note: a.this == a['this'] == a(this)")
print()
#THIS THIS THIS True Note: a.this == a['this'] == a(this)
#unlike AttrDict, keys() and values() are lists
print(type(a.keys()), type(a.values()), a.keys()[0], a.values()[0], '\n\t\t Note: Return keys() and values() as lists to allow indexing via a.keys()[0]')
#<class 'list'> <class 'list'> this THIS
# Note: Return keys() and values() as lists to allow indexing via a.keys()[0]
a.that='THAT'
#unlike attrDict, returns multiple values if asked
print(a('this', 'that'), "\n\t\t Note: a('this','that') returns a list")
print()
#new to attrThing:
#settable after the fact as in vPython
a(one=1,two=2,three=3)
print(a, '\n\t\t Note: Settable after the fact via a(one=1,two=2,three=3)')
print()
# Note: Return keys() and values() as lists to allow indexing via a.keys()[0]
#['THIS', 'THAT']
# Note: a('this','that') returns a list
a.l=[1,2,3]
a.t=(1,2,3)
print(a.l, 'should be list !!!') #note
#[1, 2, 3] should be list !!!
print(a.l, a('l'), a['l'], a.l ==a('l')==a['l'], 'all accessors return the same thing')
#[1, 2, 3] [1, 2, 3] [1, 2, 3] True all accessors return the same thing
a.l.append('this fails with the more sophisticated but mysterious https://pypi.python.org/pypi/attrdict')
print( a.l )