classification
Title: dict.fromkeys() should not cross reference mutable value by default
Type: behavior Stage:
Components: Interpreter Core Versions: Python 2.6
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: benjamin.peterson, georg.brandl, maxlem, r.david.murray, rhettinger
Priority: normal Keywords:

Created on 2009-08-18 20:28 by maxlem, last changed 2009-08-24 22:11 by georg.brandl. This issue is now closed.

Messages (6)
msg91714 - (view) Author: Maxime Lemonnier (maxlem) Date: 2009-08-18 20:28
Consider the following code sample :

keys = ['x', 'y', 'z']
d = dict.fromkeys(keys, [])
d['x'].append('dont')
d['y'].append('mix')
d['z'].append('me!')
print d['x']

>>> ['dont', 'mix', 'me!']

It is very unatural and dangerous to have all dict keys poining to the
same mutable object reference. 

The way it should behave : 
if value is mutable, create a new copy of value for each keys
else, it doesn't matter
msg91715 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2009-08-18 20:34
This behavior will never change. Use collections.defaultdict for your case.
msg91716 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-08-18 20:48
I concur.
msg91717 - (view) Author: Maxime Lemonnier (maxlem) Date: 2009-08-18 20:52
It does not suits my needs.

what if keys are passed as an argument?

I have to add this ugly loop :

for key in keys:
    d[key] = [] 
    

I really think that :
1) The doc should warn about it, since it is a very weird behaviour
2) There could at least be a third argument allowing the user to choose,
with a default value that maintains the current behaviour (for
compatibility, even though I think the default should be a deep copy)
msg91718 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2009-08-18 21:13
The docs very clearly say the second argument is the value that will be
assigned to the keys.  It doesn't matter whether or not that object is
mutable, it is that object that gets assigned.  This is just the way
that Python works:

>>> a = b = []
>>> a.append(1)
>>> b.append(2)
>>> a
[1, 2]

That is exactly analogous to what you are doing in your example.
msg91943 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2009-08-24 22:11
I'll add a bit of explanation as well:

> I have to add this ugly loop :
> 
> for key in keys:
>    d[key] = [] 
 
With a defaultdict, you don't -- you just use d[key], and if not already
present, the entry will be initialized with an empty list.

> I really think that :
> 1) The doc should warn about it, since it is a very weird behaviour

It is not weird in Python.  Implicit copies are never made, partly
because it's very hard to implement it correctly for every object.

> 2) There could at least be a third argument allowing the user to choose,
> with a default value that maintains the current behaviour (for
> compatibility, even though I think the default should be a deep copy)

The interface of a fundamental type like "dict" will not be changed
lightly. Usually, when a change is made, it is to add a feature that is
often requested by lots of users -- I've never seen someone request this
before.  And as we've seen, it can easily be written either using a
simple loop, or a defaultdict, depending on the exact use-case.
History
Date User Action Args
2009-08-24 22:11:53georg.brandlsetnosy: + georg.brandl
messages: + msg91943
2009-08-18 21:13:39r.david.murraysetnosy: + r.david.murray
messages: + msg91718
2009-08-18 20:52:49maxlemsetmessages: + msg91717
2009-08-18 20:48:02rhettingersetnosy: + rhettinger
messages: + msg91716
2009-08-18 20:34:50benjamin.petersonsetstatus: open -> closed

nosy: + benjamin.peterson
messages: + msg91715

resolution: wont fix
2009-08-18 20:28:53maxlemcreate