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.
>>> 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
inlist
- Remove the first
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.
>>> 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.
>>> a, b = [1], [1]
>>> a == b
True
>>> a is b
False
Identity Operators
<exp0> is <exp1>
evaluates to True
if both <exp0>
and <exp1>
evaluates to the same object
<exp0> is not <exp1>
does the opposite
Equality Operators
<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
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 listb
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.
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 namebalance
, the binding is changed in the first frame in whichbalance
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.