Dictionary of lists strange behaviour

C

Ciccio

Hi all,

hope you can help me understanding why the following happens:

In [213]: g = {'a': ['a1','a2'], 'b':['b1','b2']}
In [214]: rg = dict.fromkeys(g.keys(),[])
In [215]: rg
Out[215]: {'a': [], 'b': []}
In [216]: rg['a'].append('x')
In [217]: rg
Out[217]: {'a': ['x'], 'b': ['x']}

What I meant was appending 'x' to the list pointed by the key 'a' in the
dictionary 'rg'. Why rg['b'] is written too?

Thanks.
 
M

Matteo Landi

Hi all,

hope you can help me understanding why the following happens:

In [213]: g = {'a': ['a1','a2'], 'b':['b1','b2']}
In [214]: rg = dict.fromkeys(g.keys(),[])

The argument you pass which is used to fill the values of the new
dict, is created once; this means that the empty list is shared
between all the keys of the dict.
I think you need to create the new dict by hand.

Regards,
Matteo
In [215]: rg
Out[215]: {'a': [], 'b': []}
In [216]: rg['a'].append('x')
In [217]: rg
Out[217]: {'a': ['x'], 'b': ['x']}

What I meant was appending 'x' to the list pointed by the key 'a' in the
dictionary 'rg'. Why rg['b'] is written too?

Thanks.
 
J

Jean-Michel Pichavant

Ciccio said:
Hi all,

hope you can help me understanding why the following happens:

In [213]: g = {'a': ['a1','a2'], 'b':['b1','b2']}
In [214]: rg = dict.fromkeys(g.keys(),[])
In [215]: rg
Out[215]: {'a': [], 'b': []}
In [216]: rg['a'].append('x')
In [217]: rg
Out[217]: {'a': ['x'], 'b': ['x']}

What I meant was appending 'x' to the list pointed by the key 'a' in
the dictionary 'rg'. Why rg['b'] is written too?

Thanks.

rg = dict.fromkeys(g.keys(),[])

you are intialising the content with the same object [].

write instead

for key in g:
rg[key] = [] # python create a new list everytime it hits this line

For the same reason you never assign an empty list to default parameters
value:

In [37]: def a(p=[]):
....: return p
....:

In [38]: a1 = a()

In [39]: a2 = a()

In [40]: id(a1) ; id(a2)
Out[40]: 161119884
Out[40]: 161119884

Jean-Michel
 
D

Dave Angel

Hi all,

hope you can help me understanding why the following happens:

In [213]: g = {'a': ['a1','a2'], 'b':['b1','b2']}
In [214]: rg = dict.fromkeys(g.keys(),[])
In [215]: rg
Out[215]: {'a': [], 'b': []}
In [216]: rg['a'].append('x')
In [217]: rg
Out[217]: {'a': ['x'], 'b': ['x']}

What I meant was appending 'x' to the list pointed by the key 'a' in
the dictionary 'rg'. Why rg['b'] is written too?

Thanks.
The second argument to the fromkeys() method is an empty list object.
So that object is the value for *both* the new dictionary items. It
does not make a new object each time, it uses the same one.

You can check this for yourself, by doing
id(rg["a"]) and id(rg["b"])

I'd do something like :

rg = {}
for key in g.keys():
rg[key] = []

DaveA
 
T

Terry Reedy

Hi all,

hope you can help me understanding why the following happens:

In [213]: g = {'a': ['a1','a2'], 'b':['b1','b2']}
In [214]: rg = dict.fromkeys(g.keys(),[])

If you rewrite this as

bl = []
rg = dict.fromkeys(g.keys(),bl)

is the answer any more obvious?

In [215]: rg
Out[215]: {'a': [], 'b': []}
In [216]: rg['a'].append('x')
In [217]: rg
Out[217]: {'a': ['x'], 'b': ['x']}

What I meant was appending 'x' to the list pointed by the key 'a' in the
dictionary 'rg'. Why rg['b'] is written too?
 
C

Ciccio

Il 09/11/2010 16:47, Terry Reedy ha scritto:
Hi all,

hope you can help me understanding why the following happens:

In [213]: g = {'a': ['a1','a2'], 'b':['b1','b2']}
In [214]: rg = dict.fromkeys(g.keys(),[])

If you rewrite this as

bl = []
rg = dict.fromkeys(g.keys(),bl)

is the answer any more obvious?

It isn't since I erroneously assumed that a clone of the object would be
made in both cases.

Thanks for your help
 
T

Terry Reedy

Il 09/11/2010 16:47, Terry Reedy ha scritto:
Hi all,

hope you can help me understanding why the following happens:

In [213]: g = {'a': ['a1','a2'], 'b':['b1','b2']}
In [214]: rg = dict.fromkeys(g.keys(),[])

If you rewrite this as

bl = []
rg = dict.fromkeys(g.keys(),bl)

is the answer any more obvious?

It isn't since I erroneously assumed that a clone of the object would be
made in both cases.

I can see how you might think that, especially if you have experience
with other languages where that would be usual. In Python, the general
policy is to not copy objects unless explicitly requested. None, False,
and True cannot be copied. There is essentially never a reason to copy a
number or string or tuple.

I believe dict.fromkeys is more usually given None or 0 or '' as value
initializer. List *is* useful as an initializer for
collecitons.defaultdicts.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top