L-5

Objects, Collections, and Files

ENGG1330
Computer Programming I

HKU School of Computing and Data Science

L4 Survey Results

String method output correct
37%
Function output correct
65%
Slice output correct
76%
Not yet confident or need help
59%

421 submissions · 403–404 answers to these questions

2

List Assignment

inventory = ['key']backup = inventoryinventory[0] = 'map'print(backup)
Download: lec_05_inventory_prediction.py
Will backup contain
key or map?
Predict first, then follow the references
NamesObjectsinventorylist['key']
inventory refers to this list
NamesObjectsinventorybackuplist['key']
Assignment adds a reference to the same list
NamesObjectsinventorybackuplist['map']
The list changes; both names still refer to it
3

Value and Identity

x = ['key']y = ['key']print(x == y)
Download: lec_05_equality_intro.py

== compares values

These two lists contain the same item

x = ['key']y = ['key']print(id(x))print(id(y))
Download: lec_05_id_intro.py

id(x) identifies the object

Equal values, different objects: their IDs differ

x = ['key']y = xprint(x is y)
Download: lec_05_is_intro.py

is checks for the same object

Both names refer to the same list

Diagram labels O1, O2, … stand for objects

They are not the numbers returned by id()

4

Comparing Objects

first = [10, 20]second = [10, 20]alias = firstprint(first == second)print(first is second)print(first is alias)
Download: lec_05_equal_objects.py
NamesObjectsfirstaliassecondO1 · list[10, 20]O2 · list[10, 20]
Two list objects; one additional name
NamesObjectsfirstaliassecondO1 · list[10, 20]O2 · list[10, 20]
== compares these lists by contents: True
NamesObjectsfirstaliassecondO1 · list[10, 20]O2 · list[10, 20]
first is second: False
NamesObjectsfirstaliassecondO1 · list[10, 20]O2 · list[10, 20]
first is alias: True
O1, O2, …: symbolic object IDs
5

Shared References

first = ['key']alias = firstbefore = id(first)alias[0] = 'map'print(first, alias)print(id(first) == before)
Download: lec_05_shared_list.py
NamesObjectsfirstaliasO1 · list['key']
Mutation changes an existing object
NamesObjectsfirstaliasO1 · list['map']
The same object remains: id(first) == before
O1, O2, …: symbolic object IDs
6

Rebinding

first = ['key']alias = firstfirst = ['map']print(first)print(alias)print(first is alias)
Download: lec_05_rebind_list.py
NamesObjectsfirstaliasO1 · list['key']
Rebinding changes which object a name refers to
NamesObjectsfirstaliasO1 · list['key']O2 · list['map']
Only first moves to the new object O2
O1, O2, …: symbolic object IDs
7

String Immutability

word = 'door'other = wordword.upper()print(word, other)word = word.upper()print(word, other)
Download: lec_05_string_return_check.py
Predict both output lines
Which statement changes
what word refers to?
Explain your prediction before Run
O1, O2, …: symbolic object IDs
8

Tuples

ENGG1330
Computer Programming I

HKU School of Computing and Data Science
9

Single-Item Tuples

number = (0)packed = (0,)also_packed = 0,print(type(number))print(type(packed))print(packed, also_packed)
Download: lec_05_one_item_tuple.py
10

Tuple Concatenation

values = (1, 2)previous = valuesvalues += (3,)print(previous, values)print(values is previous)
Download: lec_05_tuple_rebinding.py
NamesObjectsvaluespreviousO1 · tuple(1, 2)
Two names refer to the same tuple
NamesObjectsvaluespreviousO1 · tuple(1, 2)O2 · tuple(1, 2, 3)
values moves to O2; previous keeps O1
O1, O2, …: symbolic object IDs
11

List Augmentation

values = [1, 2]previous = valuesvalues += [3]print(previous, values)print(values is previous)
Download: lec_05_list_augmentation.py
NamesObjectsvaluespreviousO1 · list[1, 2]
Two names refer to the same list
NamesObjectsvaluespreviousO1 · list[1, 2, 3]
O1 changes; both references stay
O1, O2, …: symbolic object IDs
12

Lists and Tuples: Check

a = [1]b = ax = (1,)y = xa += [2]x += (2,)print(b)print(y)
Download: lec_05_augmentation_check.py
Predict b and y
Why does the same +=
give different results?
Explain using objects and references
O1, O2, …: symbolic object IDs
13

Function Returns

def show_product(x, y):    print(x * y)def multiply(x, y):    return x * yresult = show_product(3, 4)print(result)result = multiply(3, 4)print(result)
Download: lec_05_return_recall.py

print shows output

return sends a value to the caller

After show_product(3, 4)Value
Output on screen12
resultNone

There is no return in this function

print(result) displays None

The screen now shows 12, then None

After multiply(3, 4)Value
Returned value12
result12

The final print displays 12

14

Packing and Unpacking

point = (1, 2, 3)x, y, z = pointprint(x, y, z)
Download: lec_05_packing_intro.py

Packing groups values in one tuple

Tuple Item 0Tuple Item 1Tuple Item 2
123

Unpacking assigns items to names, in order

xyz
123

Use one target per item

() is an empty tuple

An iterable supplies items one at a time

Tuples, strings, and ranges are iterable

15

Sequence Unpacking

x, y, z = (1, 2, 3)a, b, c = range(3)first, second = 'Hi'print(x, y, z)print(a, b, c)print(first, second)
Download: lec_05_unpack_sequences.py
16

Returning Multiple Values

def quotient_remainder(a, b):    return a // b, a % bresult = quotient_remainder(17, 5)print(result)quotient, remainder = resultprint(quotient, remainder)
Download: lec_05_two_results.py
17

Unpacking Errors

x, y = (1, 2, 3)print(x, y)
Download: lec_05_unpack_error.py
18

Unpacking in Loops

rows = ()for i in range(3):    rows += ((i, i * 2, i * 3),)print(rows)for x, y, z in rows:    print(x, y, z)
Download: lec_05_record_loop.py
19

Record Summary: Plan

values = (2, 5, 4)names = ('Ada', 'Ben')print(min(values))print(max(values))print(len(names))
Download: lec_05_summary_plan.py

min finds the smallest value; max finds the largest

ValueName
2Ada
5Ben
4Ada

Collect every value, but each name only once

Three records give two distinct names

The three summary results

MinimumMaximumNames
252

Assume at least one record

20

Record Summary: Code

def summarize(records):    values, names = (), ()    for value, name in records:        values += (value,)        if name not in names:            names += (name,)    return min(values), max(values), len(names)records = ((2, 'Ada'), (5, 'Ben'), (4, 'Ada'))print(summarize(records))
Download: lec_05_record_summary.py
21

Unpacking Arguments

def show(x, y, z):    print(x, y, z)point = (1, 2, 3)show(point)
Download: lec_05_tuple_argument_error.py

show(point) passes one tuple

The function needs three arguments

def show(x, y, z):    print(x, y, z)point = (1, 2, 3)show(1, 2, 3)show(point[0], point[1], point[2])show(*point)
Download: lec_05_unpack_call.py

*point unpacks its items into separate arguments

All three calls supply the same values

22

Point Displacement

def difference(x1, y1, z1, x2, y2, z2):    return x2 - x1, y2 - y1, z2 - z1start = (1, 2, 3)end = (4, 6, 8)print(difference(*start, *end))
Download: lec_05_vector_difference.py
23

Swapping Values

x, y = 3, 8temporary = xx = yy = temporaryprint(x, y)x, y = y, xprint(x, y)
Download: lec_05_swap_values.py
24

Tuple Immutability

pair = ([1, 2], [3, 4])pair[0] = [9, 9]print(pair)
Download: lec_05_tuple_assignment_error.py
25

Lists Inside Tuples

pair = ([1, 2], [3, 4])pair[0][0] = 9print(pair)
Download: lec_05_tuple_contains_list.py
pairO1 · tuple[0][1]O3 · list[1, 2]O4 · list[3, 4]
Tuple slots refer to the inner lists
pairO1 · tuple[0][1]O3 · list[9, 2]O4 · list[3, 4]
The inner list changes; tuple slots stay
O1, O2, …: symbolic object IDs
26

List Operations

ENGG1330
Computer Programming I

HKU School of Computing and Data Science
27

Appending Items

values = [1, 2]values.append([3, 4])print(values)print(len(values))
Download: lec_05_append_item.py
values · O1 · list1021append adds one itemHere that item is a list
Start with two outer items
values · O1 · list1021•2O2 · list3041
One new outer slot: len(values) is 3
O1, O2, …: symbolic object IDs
28

Extending Lists

values = [1, 2]values.extend([3, 4])print(values)print(len(values))
Download: lec_05_extend_items.py
values · O1 · list1021extend adds each supplied item
Start with the same two outer items
values · O1 · list10213243Two new outer slots
len(values) is 4; no inner list is added
O1, O2, …: symbolic object IDs
29

List Concatenation

values = [1, 2]alias = valuesvalues = values + [3]print(values, alias)print(values is alias)
Download: lec_05_list_addition.py
NamesObjectsvaluesaliasO1 · list[1, 2]
Both names initially reach O1
NamesObjectsvaluesaliasO1 · list[1, 2]O2 · list[1, 2, 3]
+ creates O2; assignment redirects values
O1, O2, …: symbolic object IDs
30

Deleting and Removing

values = [4, 7, 4, 9]del values[1]print(values)values.remove(4)print(values)
Download: lec_05_remove_items.py
values · O1 · list40714293
del values[1] selects index 1
values · O1 · list404192
After deletion, later items shift left
values · O1 · list404192
remove(4) selects the first matching value
values · O1 · list4091
Only the first 4 is removed
O1, O2, …: symbolic object IDs
31

Sorting in Place

values = [3, 1, 2]alias = valuesresult = values.sort()print(values, alias)print(result)
Download: lec_05_sort_in_place.py
NamesObjectsvaluesaliasO1 · list[3, 1, 2]
Two names share the unsorted list
NamesObjectsvaluesaliasO1 · list[1, 2, 3]
sort() changes O1; both names see it
NamesObjectsvaluesaliasO1 · list[1, 2, 3]result = None
The returned value is None
O1, O2, …: symbolic object IDs
32

Creating a Sorted List

values = [3, 1, 2]ordered = sorted(values)print(values)print(ordered)print(values is ordered)
Download: lec_05_sorted_copy.py
NamesObjectsvaluesO1 · list[3, 1, 2]
Start with the original ordering
NamesObjectsvaluesorderedO1 · list[3, 1, 2]O2 · list[1, 2, 3]
sorted() returns O2; O1 keeps its order
O1, O2, …: symbolic object IDs
33

Copying with Slices

original = [1, 2]copied = original[:]copied[0] = 9print(original, copied)print(original is copied)
Download: lec_05_slice_copy.py
NamesObjectsoriginalcopiedO1 · list[1, 2]O2 · list[1, 2]
A slice creates a new outer list O2
NamesObjectsoriginalcopiedO1 · list[1, 2]O2 · list[9, 2]
Replacing O2’s item leaves O1 unchanged
O1, O2, …: symbolic object IDs
34

Shallow Copies

original = [[1, 2], [3, 4]]copied = original[:]copied[0][1] = 9print(original)print(original is copied)print(original[0] is copied[0])
Download: lec_05_shallow_copy.py
originalO1 · list[0][1]O3 · list[1, 2]O4 · list[3, 4]
O1 contains references to O3 and O4
originalO1 · list[0][1]copiedO2 · list[0][1]O3 · list[1, 2]O4 · list[3, 4]
O2 gets copies of the same references
originalO1 · list[0][1]copiedO2 · list[0][1]O3 · list[1, 9]O4 · list[3, 4]
Both outer lists reach the changed O3
O1, O2, …: symbolic object IDs
35

Nested References

a = [1, 2]b = [3, 4]c = [1, a, b]d = [a, c]a[1] = 42print(c)print(d[0] is c[1])
Download: lec_05_nested_references.py
dO4 · list[0][1]cO3 · list1[1][2]O1 · list[1, 2]O2 · list[3, 4]ab
c[1] and d[0] both refer to O1
dO4 · list[0][1]cO3 · list1[1][2]O1 · list[1, 42]O2 · list[3, 4]ab
a[1] changes the shared O1
O1, O2, …: symbolic object IDs
36

Rebinding Nested Lists

a = [1, 42]c = [1, a, [3, 4]]d = [a, c]a = [5, 6]d[0] = 1c[1][1] = 3print(a)print(d)
Download: lec_05_nested_rebinding.py
dO4 · list[0][1]cO3 · list1[1][2]O1 · list[1, 42]O2 · list[3, 4]a
Start with the same shared inner list
dO4 · list[0][1]cO3 · list1[1][2]O1 · list[1, 42]O2 · list[3, 4]aO5 · list[5, 6]
a moves to the new object O5
dO4 · list1[1]cO3 · list1[1][2]O1 · list[1, 42]O2 · list[3, 4]aO5 · list[5, 6]
d[0] becomes 1; c[1] still reaches O1
dO4 · list1[1]cO3 · list1[1][2]O1 · list[1, 3]O2 · list[3, 4]aO5 · list[5, 6]
c[1][1] changes O1, not O5
O1, O2, …: symbolic object IDs
37

Shallow Copies: Check

a = [[1], [2]]b = a[:]b[0].append(9)print(a)b[0] = [7]print(a, b)
Download: lec_05_shallow_check.py

Predict both output lines

Which statement changes the shared inner list?

38

Removal During Iteration

def remove_blocked(values, blocked):    for value in values:        if value in blocked:            values.remove(value)values = [1, 2, 3, 4]blocked = [1, 2]remove_blocked(values, blocked)print(values)
Download: lec_05_remove_while_iterating.py
values · original list10213243Nextblocked = [1, 2]
Next visits index 0: value 1
values · original list203142Next2 has moved to index 0
After removing 1, Next advances to index 1
values · original list203142Next3 is not blocked
Visit 3, then advance to index 2
values · original list2031424 is not blocked
Visit 4, then finish: 2 was never checked
39

Iteration over a Copy

def remove_blocked(values, blocked):    for value in values[:]:        if value in blocked:            values.remove(value)values = [1, 2, 3, 4]blocked = [1, 2]remove_blocked(values, blocked)print(values)
Download: lec_05_remove_from_copy.py
Iteration copy10213243Nextvalues · original list10213243
Iterate over a copy; remove from the original
Iteration copy10213243Nextvalues · original list203142
Remove 1; the copy still has 2 at index 1
Iteration copy10213243Nextvalues · original list3041
Remove 2; the next copied item is 3
Iteration copy10213243values · original list3041
Visit 3 and 4; both remain in the original
40

List Comprehensions

ENGG1330
Computer Programming I

HKU School of Computing and Data Science
41

Building a List

squares = []for number in range(5):    squares.append(number * number)print(squares)
Download: lec_05_comprehension_loop.py
squares · result list[]range(5): 0, 1, 2, 3, 4
Start with an empty result list
squares · result list00number = 00 × 0 = 0
Append 0; the list now has 1 item
squares · result list0011number = 11 × 1 = 1
Append 1; the list now has 2 items
squares · result list001142number = 22 × 2 = 4
Append 4; the list now has 3 items
squares · result list00114293number = 33 × 3 = 9
Append 9; the list now has 4 items
squares · result list00114293164number = 44 × 4 = 16
Append 16; the list now has 5 items
42

Comprehension Syntax

squares = [number * number for number in range(5)]print(squares)numbers = [number for number in range(5)]print(numbers)
Download: lec_05_comprehension_basic.py
43

Filtering with Comprehensions

evens = [n for n in range(8) if n % 2 == 0]inverses = [1 / n for n in range(-2, 3) if n != 0]print(evens)print(inverses)
Download: lec_05_comprehension_filter.py
44

Building a Filtered List

values = [1, 2, 3, 4]blocked = [1, 2]kept = [value for value in values if value not in blocked]print(kept)print(values)
Download: lec_05_filter_copy.py
45

Comprehensions: Check

result = [n * n for n in range(6) if n % 2 == 1]
Write the equivalent loop, then predict the result

46

The random Module

import randomrandom.seed(7)print(random.randint(1, 6))print(random.choice(('pass', 'retry')))
Download: lec_05_random_intro.py

A module groups reusable code

import random makes its functions available

seed(7) sets a repeatable starting state

The dot selects a function from the module

randint(1, 6) chooses an integer from 1 to 6

Both limits are included

choice selects one item from a sequence

Run twice and compare the outputs

47

Random Samples

import randomrandom.seed(7)rolls = [random.randint(1, 6) for roll in range(5)]print(rolls)
Download: lec_05_random_samples.py
48

Text Files

ENGG1330
Computer Programming I

HKU School of Computing and Data Science
49

Opening and Closing Files

with open('demo.txt', 'w') as file:    print('File is open')print('The block has ended')
Download: lec_05_file_block_intro.py

open opens a file

'w' creates or replaces it; as file names the file object

Indented statements run inside the with block

The file is open here

Leaving the block closes the file

'r' opens an existing file for reading

Later examples add encoding='utf-8'

This selects the text encoding

50

File Methods

with open('demo.txt', 'w') as file:    file.write('key\nmap\n')with open('demo.txt', 'r') as file:    print(repr(file.read()))with open('demo.txt', 'r') as file:    for line in file:        print(repr(line))
Download: lec_05_file_methods_intro.py

write(text) writes a string

\n separates the two lines

read() returns all remaining text

repr makes newline characters visible

A file is iterable

for line in file: reads one line at a time

Each line can include its newline

51

Writing and Reading Text

with open('demo_ids.txt', 'w', encoding='utf-8') as file:    file.write('1001\n1002\n1003\n')with open('demo_ids.txt', 'r', encoding='utf-8') as file:    text = file.read()print(repr(text))
Download: lec_05_write_text.py
52

Reading Numbers

with open('demo_ids.txt', 'w', encoding='utf-8') as file:    file.write('1001\n1002\n1003\n')ids = ()with open('demo_ids.txt', 'r', encoding='utf-8') as file:    for line in file:        ids += (int(line.strip()),)print(ids)print(len(ids), ids[1])
Download: lec_05_read_numbers.py
53

Saving Simulated Outcomes

import randomrandom.seed(7)ids = (1001, 1002, 1003)with open('demo_results.txt', 'w', encoding='utf-8') as file:    for identifier in ids:        outcome = random.choice(('pass', 'retry'))        file.write(f'{identifier},{outcome}\n')with open('demo_results.txt', 'r', encoding='utf-8') as file:    print(file.read(), end='')
Download: lec_05_write_simulation.py
54

Complete the survey during the lecture

55

Work independently on the four challenges

56