References, (Im)mutability

Immutability: Numbers

Numbers are immutable

  • Object of type int with value 42

  • Variable a points to it (“gives it a name”)

  • The object cannot change its value - there is no method to modify an integer object

  • ⟶ The latter situation is equivalent to the former (which is the implementation)

a = 42
b = a

Both a and b refer to the same object:

print(id(a))
print(id(b))
140248719661832
140248719661832
../../../../../../_images/0250-refs-flat-deep-copy-immutability-numbers-shared.svg

Modifying An Integer In-Place? (Immutability)

id(a)
140248719661832
  • Operator += modifies an integer in-place?

  • a = a+7 modifies an integer in-place?

  • ⟶ No, both create new objects!

a += 1
id(a)         # <--- different object now
140248719661864
a = a + 7
id(a)         # <--- again, different object now
140248719662088

Immutability: Tuples

a = (42, "xy", 13)
b = a

print(id(a))
print(id(b))
140248434339648
140248434339648
  • Like lists, but immutable

  • No way to modify a tuple

    • No appending

    • No slice assignment

    • No nothing

../../../../../../_images/0250-refs-flat-deep-copy-immutability-tuples-shared.svg
  • And operator +=?

a += ('foo', 'bar')
print(a)
print(id(a))
print(id(b))
(42, 'xy', 13, 'foo', 'bar')
140248436164944
140248434339648
  • ⟶ leaves b alone

b
(42, 'xy', 13)

Mutability: Lists (1)

Lists are mutable

  • Objects can be modified

  • E.g. by using append()

a = [1, 2, 3]
b = a
b
[1, 2, 3]
  • Both now refer to the same object

  • Modify b

b.append(4)
b
[1, 2, 3, 4]
  • … and see a modified!

a
[1, 2, 3, 4]
../../../../../../_images/0250-refs-flat-deep-copy-mutability-list.svg

Mutability: Lists (2)

Danger …

  • Take care when passing complex data structures

  • Not passed by copy (as in C++)

  • Passed by reference (as in Java and C#, for example)

Solution?

  • Is copy a solution?

  • ⟶ I’d say no!

  • Being careful is a solution!

a = [1, 2, 3]
b = a[:]             # <--- copy
a.append(4)
b
[1, 2, 3]

Shallow Copy

  • A list within a list

  • Create “copy”

a = [1, [1, 2, 3], 2]
b = a[:]      # <--- copy
b
[1, [1, 2, 3], 2]
  • This is only a shallow copy

../../../../../../_images/0250-refs-flat-deep-copy-shallow-copy.svg
  • Modify a

a[1].append(4)
a
[1, [1, 2, 3, 4], 2]
  • And b?

b
[1, [1, 2, 3, 4], 2]
  • Reason: nested list has not been copied!

a[1] is b[1]
True
  • Only first level copied

  • Shallow copy

  • a[1] is a reference

  • is: object identity

Deep Copy

Solution: not easy

  • Recursive structure traversal

  • Handling every possible type

  • Dedicated module in the standard library: copy

  • Solution?

import copy
a = [1, [1, 2, 3], 2]
b = copy.deepcopy(a)
a[1] is b[1]
False