Skip to content

Mutability

Mutable Data Types

  • Lists
  • ...

Mutation

There're mutable objects in Python, lists are one of them.

A mutable object can be changed, but remained the same object.

python
>>> a = [10]
>>> b = a
>>> a.append(11)
>>> b
[10, 11]

Like this, a, b are bound to the same list. When we evaluate an expression a.append(11), we are using a method on the list object, and a is just a name.

A function can change the value of any object in its scope.

  • Because if the function has a parameter, its name is bound to the same object.

Mutation Methods (Example: List)

  • list.remove(element)
    • Remove the first element in list
  • list.pop([number])
  • list.append(element)
    • Adds one element to a list
  • list.extend(list)
    • Adds all elements in one list to another list (copying)

Slice Assignment

Replaces a slice with new values.

python
>>> s = [2, 3]
>>> t = [5, 6]
>>> s[0:0] = t # Zero length slice, so other elements are shifted
>>> s
[5, 6, 2, 3]
>>> s[3:] = t # replace s[3]
>>> s
[5, 6, 2, 5, 6]

Immutable Values

Hashable Are protected from mutation

  • Unless it contains a mutable value like (1, [2, 3])

Identity and Equality

Objects that are equal to each other may not have the same identity. Identical objects are always equal values, but they could change its value over time.

python
>>> a, b = [1], [1]
>>> a == b
True
>>> a is b
False

Identity Operators

python
<exp0> is <exp1>

evaluates to True if both <exp0> and <exp1> evaluates to the same object

python
<exp0> is not <exp1>

does the opposite

Equality Operators

python
<exp0> == <exp1>

evaluates to True if both <exp0> and <exp1> evaluates to the same value``

Mutable Default Values are dangerous

A default argument value is a part of the function value, not generated by a call. When the function containing a mutable value as a default value for its parameter is first called, it creates an mutable object. Then, in the course of calling the function multiple times, the object can be mutated but remain the same identity, as the parameter of the other calls bound to it.

Mutable Function

Lists in the outer of a higher order function can be changed in the inner function (as long as you can refer to it), because it is an mutable object.

  • You cannot do that with an Int
python
def make_withdraw(balance):
    def withdraw(amount):
        b = [balance]
        if amount > balance:
            return 'Insufficient funds'
        b[0] = b[0] - amount
        return b[0]
    return withdraw
  • b[0] = b[0] - amount is NOT an assignment statment, it's just changing the elements of the list b

nonlocal

Adding a nonlocal expression before an assignment statement tells the interpretor to change the binding of a name in the first frame in which the name is already bound.

python
def make_withdraw(balance):
    def withdraw(amount):
        nonlocal balance
        if amount > balance:
            return 'Insufficient funds'
        balance = balance - amount
        return balance
    return withdraw
  • The nonlocal statement declares that whenever we change the binding of the name balance, the binding is changed in the first frame in which balance is already bound.
  • Without nonlocal, an assignment statement would always bind a name in the first frame of the current environment.
    • nonlocal
    • global
  • Access: no need

Trivias

https://www.composingprograms.com/pages/24-mutable-data.html#the-cost-of-non-local-assignment

  • Only function calls can introduce new frames.
  • Assignment statements always change bindings in existing frames.