AttrDict icon indicating copy to clipboard operation
AttrDict copied to clipboard

why does AttrMap return a tuple when a list is stored?

Open gabyx opened this issue 9 years ago • 11 comments

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:

gabyx avatar May 08 '15 18:05 gabyx

m["a"] by the way returns the list however

gabyx avatar May 08 '15 18:05 gabyx

AttrDict also return tuple ex) ad = AttrDict({'foo': ['bar']}) assert ad.foo == ('bar') assert ad['foo'] == ['bar']

zamoa avatar Jul 28 '15 09:07 zamoa

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.

jonathanstrong avatar Jan 07 '16 15:01 jonathanstrong

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'>

jeffkayser avatar Mar 29 '16 14:03 jeffkayser

Thanks so this might be solved

gabyx avatar Apr 11 '16 21:04 gabyx

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.

H0R5E avatar Jul 21 '16 11:07 H0R5E

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.

H0R5E avatar Jul 21 '16 12:07 H0R5E

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.

jonschull avatar Feb 14 '17 22:02 jonschull

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]

jonschull avatar Jan 09 '18 18:01 jonschull

https://github.com/jonathanstrong/attr_dict

jonathanstrong avatar Jan 09 '18 18:01 jonathanstrong

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 )

jonschull avatar Jan 10 '18 16:01 jonschull