Python acting weird

Chris Rebert clp2 at rebertia.com
Sun Jul 25 20:29:33 EDT 2010


On Sun, Jul 25, 2010 at 5:08 PM, Westly Ward <sonicrules1234 at gmail.com> wrote:
> x = {"type":"folder", "name":"sonicbot", "data":[{"type":"folder",
> "name":"SonicMail", "data":[{"type":"file", "name":"bbcode.py",
> "compressed":False, "contents":"blahblahfilecontents"}]}]}
> print x
> def setindict(dictionary, keys, value) :
>    if len(keys) == 1 :
>        if keys[0].isdigit() and int(keys[0]) == len(dictionary)  :
>            dictionary.append(keys[0])
>        else :
>            dictionary[keys[0]] = value
>    else :
>        dictionary[keys[0]] = setindict(dictionary[keys[0]], keys[1:], value)
>    return dictionary
> a = x.copy()
>
> print id(a), id(x)
> y = setindict(a, ["data", 0, "data", 0, "compressed"], True)
> if y == x :
>    print True
>
> else :
>    print False
> print x
> print a
>
> How are x and a are ending up the same?  I would think .copy() would
> make it completely seperate.

Nope, .copy() only makes a "shallow" copy of the outermost dict, not a
recursive "deep" copy; the 2 dictionaries initially point to the
*exact* same objects for their keys and values.

a = x.copy()
assert x is not a # wouldn't be much of a copy otherwise
assert x["data"] is a["data"] # no recursive copying though
# separate dicts, so rebinding the items of one doesn't affect the other
x[42] = 12
assert 42 not in a
# mutating the items in one does affect the other however
x["data"].append(7)
assert a["data"].pop() == 7
# remember that x[y] = z === x.__setitem__(y, z)
# so we can write a similar example thusly:
x["data"][0] = 8
assert a["data"][0] == 8
# again, rebinding at the outermost level is independent
x["data"] = 99
assert a["data"] != x["data"]

For a deep copy, use copy.deepcopy() in the std lib:
http://docs.python.org/library/copy.html#copy.deepcopy

Cheers,
Chris
--
http://blog.rebertia.com



More information about the Python-list mailing list