Python: Mutable vs. Immutable

This is part of my Python & Django Series which can be found here including information on how to download all the source code.

Object Id & Type

All types in Python are considered a class, every variable therefore holds an object instance. When it is initiated it is assigned a unique object id. It’s type is defined at runtime and once set can never change, however it’s state can be changed if it is mutable.

The id() function can be used to get the id of an object while the type() function can be used to get the type of an object.

Code:

print(type("foo"))
print(id("foo"))

Output:

3065278112

Mutable vs. Immutable

Developers from other languages such as Java or C# might be familiar with the terms primitive or value types along with their counter part reference types, as well as the ability to pass these by either value or reference.

So what about Python?

Python passes all types by reference however their semantics vary depending upon if their type is immutable or mutable.

  • Immutable objects cannot have their state changed and include:
    • Numbers
    • Strings
    • Tuples
    • Booleans
  • Mutable objects can have their state changed and include:
    • Dictionaries
    • Lists

Immutable

Immutable objects appearing to be modified are actually new object instances being created and assigned onto the existing reference pointer or variable.

The following code creates a variable with a value of one, we then add one onto it. The addition does not modify the immutable object instance, it creates a new one and swaps them over.

Code:

one = 1
print(id(one))

one += 1
print(id(one))

Output:

138251904
138251920

Interestingly, the creation of two immutable objects of the same type and state will only create one object instance in memory; they will both point to the same data. This is to save on memory.

For example if we create two variables with the integer value of 1, both references point to the same integer instance of value 1.

Code:

a = 1
b = 1

print(id(a))
print(id(b))

Output:

138251904
138251904

Mutable

Where an instance of a mutable object type is created and assigned to two variables they both share the same object instance and as such have the same object id. Changing the state via either variable affects both variables because they are both referencing or pointing to the same object.

Code:

one = [1, 2, 3]
two = one

one.append(4)

print(id(one), one)
print(id(two), two)

Output:

3072788684 [1, 2, 3, 4]
3072788684 [1, 2, 3, 4]

Only reassigning or perhaps cloning an object instance can break their link.

Code:

one = [1, 2, 3]
two = one
one.append(4)
two = [1, 2, 3]

print(id(one), one)
print(id(two), two)

Output:

3072788684 [1, 2, 3, 4]
3072788656 [1, 2, 3]

Mixing Mutable & Immutable

In Python a variable is always reference to a type. Where an immutable type is modified, the interpreter actually creates a new object (if it is unique) which is then referenced. Lets take the following example:

Code:

name = 'Fred'
names = [name]
more_names = names
more_names.append('George')
name = 'Bill'

print("Name:", name)
print("Names:", names)
print("More Names:", more_names)

Which will output:

Output:

Name: Bill
Names: [‘Fred’, ‘George’]
More Names: [‘Fred’, ‘George’]

The variable name is added into the list and then later changed. As strings are immutable a new string is created and referenced by name. The single list instance, referenced by names and more_names, are still pointing to the original string instance.

As Function Arguments

In Python, the method arguments or parameters are neither passed by value or by reference, they are passed by assignment.

For .NET developers this works similar to passing by value of reference types where a copy of the reference is created and passed into the function.

Changing The Value

When a method parameter has its state or value changed, the effect outside of the method is different depending upon if it is an immutable or mutable type.

Changes to a mutable type take effect outside of the method.

Code:

def append_value(a_data, to_input):
    a_data.extend(to_input)
    print("Inside append_value:", a_data)

mutable_data = []
print("Before append_value:", mutable_data)
append_value(mutable_data, (1, 2, 3))
print("After append_value:", mutable_data)

Output:

Before append_value: []
Inside append_value: [1, 2, 3]
After append_value: [1, 2, 3]

Changes to an immutable type actually recreate the object instance, the reference is then reassigned to the new instance. Changes do not take effect outside of the method.

Code:

def increment(an_int):
    an_int += 1
    print("Inside increment:", an_int)

data = 1
print("Before increment:", data)
increment(data)
print("After increment:", data)

Output:

Before increment: 1
Inside increment: 2
After increment: 1

Reassigning A Reference

Reassigning the reference to either an immutable or mutable data type does not change the value outside of the method; it simply reassigns an object instance to the copy of the reference which is passed into the method.

The following method simply assigns the value of one parameter onto another.

Code:

def assign_value(a_data, to_assign):
    a_data = to_assign
    print("Inside append_value:", a_data)

Regardless if we are using mutable or immutable types, the change does not take effect outside of the method.

Code:

mutable_data_two = []
print("Before assign_value:", mutable_data_two)
assign_value(mutable_data_two, [1, 2, 3])
print("After assign_value:", mutable_data_two)

Output:

Before assign_value: []
Inside append_value: [1, 2, 3]
After assign_value: []

Code:

immutable_data = "old value"
print("Before assign_value:", immutable_data)
assign_value(immutable_data, "new value")
print("After assign_value:", immutable_data)

Output:

Before assign_value: old value
Inside append_value: new value
After assign_value: old value

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s