1 of 43

Lecture 23

Mutable Data�

2 of 43

reminder/Announcement

  • Mic
  • HW10 for Extra credit: due Tuesday of Final week.
    • Pytests
    • Sets
    • Abstraction barriers

3 of 43

Mutability

  • Immutable – the value of the object cannot be changed:
    • integers, floats, booleans
    • strings, tuples
  • Mutable – the value of the object can be changed:
    • Lists
    • Dictionaries

4 of 43

Mutability

  • Immutable – the value of the object cannot be changed:
    • integers, floats, booleans
    • strings, tuples
  • Mutable – the value of the object can be changed:
    • Lists
    • Dictionaries

5 of 43

Mutability

  • Immutable – the value of the object cannot be changed:
    • integers, floats, booleans
    • strings, tuples
  • Mutable – the value of the object can be changed:
    • Lists
    • Dictionaries

6 of 43

Observation:Demo

  • The same object can change the value during the course of computation.
  • All names that refer to the same object are affected by a mutation.

7 of 43

Mutation Can Happen Within a Function Call

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

>>> four = [1, 2, 3, 4]

8 of 43

Mutation Can Happen Within a Function Call

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

>>> four = [1, 2, 3, 4]

>>> len(four)

4

9 of 43

Mutation Can Happen Within a Function Call

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

>>> four = [1, 2, 3, 4]

>>> len(four)

4

>>> mystery(four)

10 of 43

Mutation Can Happen Within a Function Call

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

>>> four = [1, 2, 3, 4]

>>> len(four)

4

>>> mystery(four)

>>> len(four)

2

11 of 43

Mutation Can Happen Within a Function Call

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

>>> four = [1, 2, 3, 4]

>>> len(four)

4

>>> mystery(four)

>>> len(four)

2

def mystery(s):

s.pop()

s.pop()

12 of 43

Mutation Can Happen Within a Function Call. demo

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

>>> four = [1, 2, 3, 4]

>>> len(four)

4

>>> mystery(four)

>>> len(four)

2

def mystery(s):

s.pop()

s.pop()

def mystery(s):

s[2:] = []

13 of 43

Mutation Can Happen Within a Function Call. demo

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

>>> four = [1, 2, 3, 4]

>>> len(four)

4

>>> another_mystery() # no arguments

>>> len(four)

2

def mystery(s):

s.pop()

s.pop()

14 of 43

Mutation Can Happen Within a Function Call. demo

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

>>> four = [1, 2, 3, 4]

>>> len(four)

4

>>> another_mystery() # no arguments

>>> len(four)

2

def mystery(s):

s.pop()

s.pop()

def another_mystery():

four.pop()

four.pop()

15 of 43

How about tuples?

>>> four = (1, 2, 3, 4)

>>> len(four)

4

>>> mystery(four)

>>> four

What is four?

A: (1, 2, 3, 4)

B: (1, 2)

C: ( )

D: Something else

def mystery(s):

s[2:] = ( )

16 of 43

From value to storage …

  • A variable assigned a compound value (object) is a reference (memory address) to that object.
  • Mutable object can be changed but the variable(s) still refer to it

17 of 43

Explanation (board)

18 of 43

From value to storage …

  • A variable assigned a compound value (object) is a reference to that object.
  • Mutable object can be changed but the variable(s) still refer to it

2

x = [1, 2, 3]

y = 6

x [1] = y

x [1]

19 of 43

From value to storage …

  • A variable assigned a compound value (object) is a reference to that object.
  • Mutable object can be changed but the variable(s) still refer to it

20 of 43

Parameter passing: Output?

def test(x):

x = x + 1

y = 10

test(y)

print(y)

A: 10

B: 11

C: None

D: Error

E: I do not know

21 of 43

Parameter passing: Output?

def test(x):

x[0] = x[0] + 1

y = [1, 2, 3]

test(y)

print(y)

A: [1, 2, 3]

B: [2, 2, 3]

C: None

D: Error

E: I do not know

22 of 43

What is the output?

var = ([1, 2], [3, 4])

copy = var

var[0][1] = "changed"

var = ([1, "changed"], [3, 4])

print(copy is var)

A : Error

B: None

C: ([1, “changed”], [3, 4])

D: True

E: False

23 of 43

What is the output?

def func(lst):

new_list = lst

new_list[0] = ‘m’

param = “string”

func(param)

print(param)

A : Error

B: None

C: string

D: mtring

E: None of the above

24 of 43

mic

25 of 43

What is the output?

def func(lst):

new_list = lst

new_list[0] = 'changed'

param = [1, 2, 3, 4, 5]

func(param)

print(param)

A : Error

B: None

C: [1, 2, 3, 4, 5]

D: [“changed”, 2, 3, 4, 5]

E: None of the above

26 of 43

Examples

x = [1, 2 , 3]

y = x

print (y)

x[1] = 11

print (y)

x = [1, 2]

y = [x, x, x]

print (y)

x[1] = 3

print (y)

y[2] = [1, 3]

print (y[0] == y[2])

print (y[0] is y[2])

27 of 43

x = [1, 2 , 3]

y = x

print (y)

x[1] = 11

print (y)

28 of 43

x = [1, 2]

y = [x, x, x]

print (y)

x[1] = 3

print (y)

y[2] = [1, 3]

print (y[0] == y[2])

print (y[0] is y[2])

29 of 43

Copies, ‘is’ and ‘==‘

>>> alist = [1, 2, 3, 4]

>>> alist == [1, 2, 3, 4] # Equal values?

True

>>> alist is [1, 2, 3, 4] # same object?

False

>>> blist = alist # assignment refers

>>> alist is blist # to same object. Shallow copy

True

>>> blist = list(alist) # type constructors copy

>>> blist is alist

False

30 of 43

Copies, ‘is’ and ‘==‘

>>> alist = [1, 2, 3, 4]

>>> alist == [1, 2, 3, 4] # Equal values?

True

False

>>> blist = alist[ : ] #integers

>>> blist is alist

>>> blist

[1, 2, 3, 4]

31 of 43

Copies, ‘is’ and ‘==‘

>>> alist = [[1], [2], [3], [4]]

>>> blist = alist[:]

>>> blist[0][0] = 10

>>> alist is blist

False

But try to print these lists.

32 of 43

Real deep copy

import copy

list_a = [[1, 2], [3, 4], [5, 6]]

list_b = copy.deepcopy(list_a)

# Modify an element in the copied list

list_b[0][0] = 10

print(list_a) # Output: [[1, 2], [3, 4], [5, 6]]

print(list_b) # Output: [[10, 2], [3, 4], [5, 6]]

33 of 43

Identity Operators: is is not

34 of 43

Identity Operators

35 of 43

Identity Operators

Identical objects are always equal values

36 of 43

In-place algorithm

  • Problem to solve: Given a list of integers. Change every even number to an odd one, so that your function mutates the list.

37 of 43

In-place algorithm

  • Problem to solve: Given a list of integers. Change every even number to an odd one, so that your function mutates the list.

def to_odd(lst):

output = []

for i in lst:

if i%2==0:

output.append(i+1)

else:

output.append(i)

return output

Not in place!��Another list of the O(n) space is used.

38 of 43

In-place algorithm

  • Problem to solve: Given a list of integers. Change every even number to an odd one, so that your function mutates the list.

def to_odd(lst):

for i in range(len(lst)):

if lst[i]%2==0:

lst[i] = lst[i] + 1

In place!��No other space besides a given list was used.

39 of 43

def to_odd(lst):

output = []

for i in lst:

if i%2==0:

output.append(i+1)

else:

output.append(i)

return output

>>> lst = [1,2,3]

>>> to_odd(lst)

>>> print(lst)

[1, 2, 3] #values are NOT changed

def to_odd(lst):

for i in range(len(lst)):

if lst[i]%2==0:

lst[i] = lst[i] + 1

>>> lst = [1,2,3]

>>> to_odd(lst)

>>> print(lst)

[1, 3, 3] #values are changed

40 of 43

Mutable Default Arguments are Dangerous

  • A default argument value is part of a function value, not generated by a call

41 of 43

Mutable Default Arguments are Dangerous

  • A default argument value is part of a function value, not generated by a call

What will happen if I call f()again?

A: 1

B: 2

C: 3

D: 0

E: Error

42 of 43

43 of 43