Be careful with using dict() to create a copy

09 September 2015   7 comments   Python

Everyone who's done Python for a while soon learns that dicts are mutable. I.e. that they can change.

One way of "forking" a dictionary into two different ones is to create a new dictionary object with dict(). E.g:

>>> first = {'key': 'value'}
>>> second = dict(first)
>>> second['key'] = 'other'
>>> first
{'key': 'value'}
>>> second
{'key': 'other'}

See, you can change the value of a key without affecting the dictionary it came from.

But, if one of the values is also mutable, beware!

>>> first = {'key': ['value']}
>>> second = dict(first)
>>> second['key'].append('second value')
>>> first
{'key': ['value', 'second value']}
>>> second
{'key': ['value', 'second value']}

This is where you need to use the built in copy.deepcopy.

>>> import copy
>>> first = {'key': ['value']}
>>> second = copy.deepcopy(first)
>>> second['key'].append('second value')
>>> first
{'key': ['value']}
>>> second
{'key': ['value', 'second value']}

Yay! Hope it helps someone avoid some possibly confusing bugs some day.

UPDATE

As ëRiC reminded me, there are actually three ways to make a "shallow copy" of a dictionary:

1) some_copy = dict(some_dict)

2) some_copy = some_dict.copy()

3) some_copy = copy.copy(some_dict) # after importing 'copy'

Comments

aRkadeFR

wow... thanks for the post :)

ëRiC

Dicts have a copy method right away!

    d = {'asdf': ['value']}
    dd = d.copy()
    dd['asdf'] = ['other']
    d
    # Result: {'asdf': ['value']} #
    dd
    # Result: {'asdf': ['other']} #

(can it really be!? I contributed something?!! :D)

Peter Hansen

Note that using obj.copy() should probably be preferred to dict(obj) since the former approach allows someone to override the copy operation in subclasses whereas the latter does not.

Although by default obj.copy() returns a dict, if someone has chosen to return an instance of the type(obj) then only that approach will have the intended result. (As with any such advice, this applies to the general case but not, by definition, if you have a good reason to do it differently.)

James

Awesome post thank you

David

Thank YOU so much!

Ilya Rusin

copy.deepcopy is way slower than dict comprehension -
https://stackoverflow.com/questions/20772885/is-python-deepcopy-more-efficient-than-custom-code-or-less-efficient-predictabl

"The reasons why deepcopy is so much slower than the dict comprehension + list copy are:

 - deepcopy is multi-purpose function - it works for mostly any kind of object
 - deepcopy is implemented in python whilst dict comprehension and list slicing is done at lower level

And most imporantly:

 - deepcopy makes copies of the elements inside containers recursively, whilst your dict comprehension does not."

Peter Bengtsson

But if it's the same object (after a dict comprehension spin), then it risky that it's mutable and gets changed by something that you didn't expect. That's functionally different so it can't really be compared performance-wise.

Your email will never ever be published


Related posts

Previous:
Examples of mozjpeg savings 01 September 2015
Next:
peepin - a great companion to peep 10 September 2015
Related by Keyword:
Items function in JavaScript for looping over dictionaries like Python 23 February 2018
Fastest way to uniquify a list in Python >=3.6 23 December 2017
Web Console trick to get all URLs into your clipboard 27 April 2017
When to __deepcopy__ classes in Python 14 March 2012
Pleonasm 11 June 2006
Related by Text:
jQuery and Highslide JS 08 January 2008
I'm back! Peterbe.com has been renewed 05 June 2005
Anti-McCain propaganda videos 12 August 2008
Ever wondered how much $87 Billion is? 04 November 2003
Guake, not Yakuake or Yeahconsole 23 January 2010