Velocity Reviews > Dictionary of lists strange behaviour

# Dictionary of lists strange behaviour

Ciccio
Guest
Posts: n/a

 11-09-2010
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.

Matteo Landi
Guest
Posts: n/a

 11-09-2010
On Tue, Nov 9, 2010 at 3:14 PM, Ciccio <> wrote:
> 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.
> --
> http://mail.python.org/mailman/listinfo/python-list
>

--
Matteo Landi
http://www.matteolandi.net/

Jean-Michel Pichavant
Guest
Posts: n/a

 11-09-2010
Ciccio wrote:
> 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 [].

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

Dave Angel
Guest
Posts: n/a

 11-09-2010

On 2:59 PM, Ciccio wrote:
> 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

Terry Reedy
Guest
Posts: n/a

 11-09-2010
On 11/9/2010 9:14 AM, Ciccio wrote:
> 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?

--
Terry Jan Reedy

Ciccio
Guest
Posts: n/a

 11-09-2010
Il 09/11/2010 16:47, Terry Reedy ha scritto:
> On 11/9/2010 9:14 AM, Ciccio wrote:
>> 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

Ciccio
Guest
Posts: n/a

 11-09-2010
Thank you all, this was timely and helpful.
francesco

Terry Reedy
Guest
Posts: n/a

 11-09-2010
On 11/9/2010 12:19 PM, Ciccio wrote:
> Il 09/11/2010 16:47, Terry Reedy ha scritto:
>> On 11/9/2010 9:14 AM, Ciccio wrote:
>>> 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

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.

--
Terry Jan Reedy

John Posner
Guest
Posts: n/a

 11-09-2010
On 11/9/2010 1:43 PM, Terry Reedy wrote:
> ... List *is* useful as an initializer for
> collecitons.defaultdicts.

And it was useful when several members of this forum helped me to
develop a prime-number generator.

See http://www.mail-archive.com/python-l...msg288128.html.

(I meant to post this to the "functions, list, default parameters"
thread, but never got a round tuit.)

-John