Search code examples
pythonimmutabilityhashable

Check for mutability in Python?


Consider this code:

a = {...} # a is an dict with arbitrary contents
b = a.copy()
  1. What role does mutability play in the keys and values of the dicts?
  2. How do I ensure changes to keys or values of one dict are not reflected in the other?
  3. How does this relate to the hashable constraint of the dict keys?
  4. Are there any differences in behaviour between Python 2.x and Python 3.x?

How do I check if a type is mutable in Python?


Solution

    1. Keys must be hashable - that's all that's forced upon you. In particular, you can have a user-defined class whose instances are hashable but also mutable - but this is generally a bad idea.

    2. By not sharing values between the two dicts. It's generally OK to share the keys, because they should be immutable (and will be, for built-in types). Copying the dictionary, in the sense of the copy standard library module, is definitely safe. Calling the dict constructor here works, too: b = dict(a). You could also use immutable values.

    3. All built-in immutable types are hashable. All built-in mutable types are not hashable. The constraint on dict keys simply requires that the built-in hash function works on the key, which in turn requires that its class implements the __hash__ magic method.

      However, the code may break subtly or unexpectedly if an object's hash could ever change during its lifetime. For a pathological example:

      >>> import random
      >>> class x:
      ...     def __hash__(self): return random.randint(0, 100)
      ... 
      >>> a, b = x(), x()
      >>> c = {a:1, b:2}
      >>> c[a]
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      KeyError: <__main__.x object at ...>
      

      This is why trying to make a mutable, hashable type is ill-advised: the hash is expected not to change, but is also expected to reflect the state of the object.

    4. No.

    A type is mutable if it is not immutable. A type is immutable if it is a built-in immutable type: str, int, long, bool, float, tuple, and probably a couple others I'm forgetting. User-defined types are always mutable.

    An object is mutable if it is not immutable. An object is immutable if it consists, recursively, of only immutable-typed sub-objects. Thus, a tuple of lists is mutable; you cannot replace the elements of the tuple, but you can modify them through the list interface, changing the overall data.