dict keys must be hashable, which implies they have an immutable hash value. dict values may or may not be mutable; however, if they are mutable this impacts your second question.
"Changes to the keys" will not be reflected between the two dicts. Changes to immutable values, such as strings will also not be reflected. Changes to mutable objects, such as user defined classes will be reflected because the object is stored by id (i.e. reference).
class T(object):
def __init__(self, v):
self.v = v
t1 = T(5)
d1 = {'a': t1}
d2 = d1.copy()
d2['a'].v = 7
d1['a'].v # = 7
d2['a'] = T(2)
d2['a'].v # = 2
d1['a'].v # = 7
import copy
d3 = copy.deepcopy(d2) # perform a "deep copy"
d3['a'].v = 12
d3['a'].v # = 12
d2['a'].v # = 2
I think this is explained by the first two answers.
Not that I know of in this respect.
some additional thoughts:
There are two main things to know for understanding the behavior of keys: keys must be hashable (which means they implement object.__hash__(self)
) and they must also be "comparable" (which means they implement something like object.__cmp__(self)
). One important take-away from the docs: by default, user-defined objects' hash functions return id()
.
Consider this example:
class K(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return self.x + self.y
k1 = K(1, 2)
d1 = {k1: 3}
d1[k1] # outputs 3
k1.x = 5
d1[k1] # KeyError! The key's hash has changed!
k2 = K(2, 1)
d1[k2] # KeyError! The key's hash is right, but the keys aren't equal.
k1.x = 1
d1[k1] # outputs 3
class NewK(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return self.x + self.y
def __cmp__(self, other):
return self.x - other.x
nk1 = NewK(3, 4)
nd1 = {nk1: 5}
nd1[nk1] # outputs 5
nk2 = NewK(3, 7)
nk1 == nk2 # True!
nd1[nk2] # KeyError! The keys' hashes differ.
hash(nk1) == hash(nk2) # False
nk2.y = 4
nd1[nk2] # outputs 5
# Where this can cause issues:
nd1.keys()[0].x = 5
nd1[nk1] # KeyError! nk1 is no longer in the dict!
id(nd1.keys()[0]) == id(nk1) # Yikes. True?!
nd1.keys()[0].x = 3
nd1[nk1] # outputs 5
id(nd1.keys()[0]) == id(nk1) # True!
Values are much easier to understand, the dict stores references to objects. Read the sections on hashable. Things like strings are immutable, if you "change" them, the dict you changed it in now references a new object. Objects which are mutable can be "changed in-place", hence the value of both dicts will change.
d1 = {1: 'a'}
d2 = d1.copy()
id(d1[1]) == id(d2[1]) # True
d2[1] = 'z'
id(d1[1]) == id(d2[1]) # False
# the examples in section 2 above have more examples of this.
Anyway, here are the main points of all this:
- For keys, it may not be mutability, but rather hashability and comparability, that you care about.
- You care about mutability for values, because by definition, a mutable object's value can be changed without changing the reference to it.
I do not think there is a general way to test either of those points. The tests for suitability would depend on your use-case. For instance, it may be sufficient to check that an object does or does not implement __hash__
and comparison (__eq__
or __cmp__
) functions. Like-wise, you might be able to "check" an object's __setattr__
method in some way to determine if it is mutable.