Dictionary of lists strange behaviour

Discussion in 'Python' started by Ciccio, Nov 9, 2010.

  1. Ciccio

    Ciccio Guest

    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.
     
    Ciccio, Nov 9, 2010
    #1
    1. Advertising

  2. Ciccio

    Matteo Landi Guest

    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/
     
    Matteo Landi, Nov 9, 2010
    #2
    1. Advertising

  3. 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 [].

    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
     
    Jean-Michel Pichavant, Nov 9, 2010
    #3
  4. Ciccio

    Dave Angel Guest

    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
     
    Dave Angel, Nov 9, 2010
    #4
  5. Ciccio

    Terry Reedy Guest

    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
     
    Terry Reedy, Nov 9, 2010
    #5
  6. Ciccio

    Ciccio Guest

    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
    made in both cases.

    Thanks for your help
     
    Ciccio, Nov 9, 2010
    #6
  7. Ciccio

    Ciccio Guest

    Thank you all, this was timely and helpful.
    francesco
     
    Ciccio, Nov 9, 2010
    #7
  8. Ciccio

    Terry Reedy Guest

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

    --
    Terry Jan Reedy
     
    Terry Reedy, Nov 9, 2010
    #8
  9. Ciccio

    John Posner Guest

    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//msg288128.html.

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

    -John
     
    John Posner, Nov 9, 2010
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ilias Lazaridis
    Replies:
    6
    Views:
    445
    Ilias Lazaridis
    Feb 21, 2006
  2. =?UTF-8?B?w4FuZ2VsIEd1dGnDqXJyZXogUm9kcsOtZ3Vleg==

    List of lists of lists of lists...

    =?UTF-8?B?w4FuZ2VsIEd1dGnDqXJyZXogUm9kcsOtZ3Vleg==, May 8, 2006, in forum: Python
    Replies:
    5
    Views:
    410
    =?UTF-8?B?w4FuZ2VsIEd1dGnDqXJyZXogUm9kcsOtZ3Vleg==
    May 15, 2006
  3. james_027
    Replies:
    1
    Views:
    329
    Marc 'BlackJack' Rintsch
    Aug 22, 2007
  4. Navkirat Singh
    Replies:
    6
    Views:
    3,075
    Navkirat Singh
    Jul 29, 2010
  5. Chris Rebert
    Replies:
    0
    Views:
    529
    Chris Rebert
    Jul 29, 2010
Loading...

Share This Page