Programming for Data Processing

Lectures: Nguyễn Tuấn Phong, Đào Việt Anh

Labs: Trịnh Ngọc Huỳnh, Phạm Tiến Du

Semester: 2025-2026-1

Institute for AI, VNU-UET

Lecture 2:
Control Flow
Data Structures

1. Branching and Iteration

Branching


if <condition>:
    <expression>
    ...
    

if <condition>:
    <expression>
    <expression>
    ...
else:
    <expression>
    <expression>
    ...
    

if <condition>:
    <expression>
    <expression>
    ...
elif <condition>:
    <expression>
    <expression>
    ...
else:
    <expression>
    <expression>
    ...
    
  • <condition> has value True or False
  • Block runs if <condition> is True

INDENTATION

  • Unlike C/C++, indentation matters in Python
  • It’s how you denote blocks of code

x = float(input("Enter a number for x: "))
y = float(input("Enter a number for y: "))

if x == y:
    print("x and y are equal")
    if y != 0:
        print("therefore, x / y is", x / y)
elif x < y:
    print("x is smaller")
else:
    print("y is smaller")

print("thanks!")
  

❓Question: Can we use spaces instead of tabs for indentation?

== versus =


x = float(input("Enter a number for x: "))
y = float(input("Enter a number for y: "))

if x == y:
    print("x and y are equal")
    if y != 0:
        print("therefore, x / y is", x / y)
elif x < y:
    print("x is smaller")
else:
    print("y is smaller")

print("thanks!")
  

What if using x = y in Line #4?

→ Get SyntaxError

Interation: while loops


while <condition>:
    <expression>
    <expression>
    ...
  
  1. <condition> evaluates to a Boolean
  2. If <condition> is True, do all steps inside the while block
  3. Check <condition> again
  4. Repeat until <condition> is False

while LOOP EXAMPLE

Question: What does this program do?


password = ""
while password != "python":
    password = input("Enter the password: ")

print("Access granted!")
  

Iteration: for LOOPS


for <variable> in range(<some_num>):
    <expression>
    <expression>
    ...
  
  1. Each time through the loop, <variable> takes a value
  2. First time, <variable> starts at the smallest value
  3. Next time, <variable> gets the previous value + 1
  4. Etc.

for LOOP EXAMPLE

Question: What does this program do?


fruits = ["apple", "banana", "cherry"]

for fruit in fruits:
    print("I like", fruit)
  


while versus for LOOPS

Iterate through numbers in a sequence:

  • More complicated with while loop
    
    n = 0
    while n < 5:
        print(n)
        n = n + 1
      
  • Shortcut with for loop
    
    for n in range(5):
        print(n)
      

range(start, stop, step)

  • Default values:
    • start = 0 and step = 1 (both optional)
  • Loop runs until value reaches stop - 1

mysum = 0
for i in range(7, 10):
    mysum += i
print(mysum)
  

mysum = 0
for i in range(5, 11, 2):
    mysum += i
print(mysum)
  

break STATEMENT

  • Immediately exits whatever loop it is in
  • Skips remaining expressions in the code block
  • Exits only the innermost loop

Question: What happens in the following code?


while <condition_1>:
    while <condition_2>:
        <expression_a>
        break
        <expression_b>
    <expression_c>
  

break STATEMENT

Question: What happens in this program?


mysum = 0

for i in range(5, 11, 2):
    mysum += i
    if mysum == 5:
        break
    mysum += 1

print(mysum)
  

continue Statement

  • Skip rest of current iteration
  • Jump to next loop cycle
  • Works in for & while

for i in range(5):
    if i == 2:            
        continue
    print(i)
  

for vs while loops

for loops while loops
Iterations known number of iterations unbounded number of iterations
Early exit can end early via break can end early via break
Skipping can skip to next iteration via continue can skip to next iteration via continue
Counters uses a counter can use a counter, but must initialize before loop and increment inside loop
Rewriting can rewrite a for loop using a while loop may not be able to rewrite a while loop using a for loop
Question
Can you think of an example where a while loop cannot be rewritten as a for loop?

2. Strings

STRINGS

  • Think of string as a sequence of case-sensitive characters
  • Can compare strings with ==, >, <, etc.
  • len() is used to retrieve the length of the string

>>> s = "abc"
>>> len(s)
3
  

STRINGS

  • Square brackets are used for indexing to access characters at a position

s = "abc"

# indexes:   0   1   2
# indexes:  -3  -2  -1

s[0]   # "a"
s[1]   # "b"
s[2]   # "c"
s[3]   # error (out of bounds)
s[-1]  # "c"
s[-2]  # "b"
s[-3]  # "a"
  

STRINGS

  • Can slice strings using [start:stop:step]
  • If given two numbers, [start:stop]
    • step = 1 by default
  • You can omit numbers and leave just colons

s = "abcdefgh"

s[3:6]     # "def", same as s[3:6:1]
s[3:6:2]   # "df"
s[::]      # "abcdefgh", same as s[0:len(s):1]
s[::-1]    # "hgfedcba", same as s[-1:-(len(s)+1):-1]
s[4:1:-2]  # "ec"
  

STRINGS

  • Strings are immutable → cannot be modified in place
  • 
    >>> s = "hello"
    
  • Attempting to change a character directly raises an error
  • 
    >>> s[0] = 'y'
    ...
    TypeError: 'str' object does not support item assignment
    
  • Reassigning with concatenation creates a new string object
  • 
    >>> s = 'y' + s[1:len(s)]
    >>> s
    'yello'
      

STRINGS AND LOOPS

  • A for loop can iterate over the characters in a string
  • These two code snippets do the same thing
  • The bottom one is more “pythonic”

s = "abcdefgh"

for index in range(len(s)):
    if s[index] == 'i' or s[index] == 'u':
        print("There is an i or u")
  

for char in s:
    if char == 'i' or char == 'u':
        print("There is an i or u")
  

3. Tuples

TUPLES

  • An ordered sequence of elements, can mix element types
  • Cannot change element values → immutable
  • Represented with parentheses

te = ()                # empty tuple
t = (2, "uet", 3)      # tuple with 3 elements
t[0]                   # 2 # first element
(2, "uet", 3) + (5, 6) # (2, "uet", 3, 5, 6)
t[1:2]                 # ("uet",)   # extra comma → tuple with one element
t[1:3]                 # ("uet", 3)
len(t)                 # 3
t[1] = 4               # ❌ error, cannot modify tuple elements
  

TUPLES

  • Conveniently used to swap variable values

❌ Incorrect


x = y
y = x
      

✅ Temp var


temp = x
x = y
y = temp
      

✅ Pythonic swap


(x, y) = (y, x)
      
  • Used to return multiple values from a function (presented next week)

4. Lists

LISTS

  • Ordered sequence of information
  • Accessible by index
  • Denoted by square brackets [ ]
  • Contains elements
    • Usually homogeneous (e.g., all integers)
    • Can contain mixed types (not common)
  • List elements can be changed → list is mutable

INDICES AND ORDERING


a_list = []         # empty list
L = [2, 'a', 4, [1, 2]]

len(L)              # 4
L[0]                # 2
L[2] + 1            # 5
L[3]                # [1, 2], another list!
L[4]                # ❌ error (index out of range)

i = 2
L[i-1]              # 'a' since L[1] = 'a'
  

CHANGING ELEMENTS

  • Lists are mutable!
  • Assigning to an element changes its value

>>> L = [2, 1, 3]
>>> L[1] = 5
>>> L
[2, 5, 3]
  

→ It’s the same object L, only modified in place.

Iterating Over a List

  • Compute the sum of elements in a list
  • Two common patterns
Index-based

total = 0
for i in range(len(L)):
    total += L[i]
print(total)
      
Direct iteration

total = 0
for x in L:
    total += x
print(total)
      
  • List indices run from 0 to len(L)-1
  • range(n) yields 0 to n-1

Operations on Lists — Add

  • Add an element to the end with L.append(elem)
  • Mutates the list

>>> L = [2, 1, 3]
>>> L.append(5)
>>> L
[2, 1, 3, 5]
  
  • What’s the dot?
    • Lists are objects (like everything else in Python)
    • Objects have data and methods
    • Call methods with object_name.do_something()
    • Will be introduced later

Operations on Lists — Combine

  • Concatenate with + to make a new list
  • Extend in place with L.extend(some_list) (mutates)

L1 = [2, 1, 3]
L2 = [4, 5, 6]
L3 = L1 + L2  # L3 == [2, 1, 3, 4, 5, 6]; L1, L2 unchanged
  

L1 = [2, 1, 3]
L1.extend([0, 6])  # L1 == [2, 1, 3, 0, 6]  (mutated)
  

Operations on Lists — Remove

  • Delete at a specific index with del L[i]
  • Remove last element with L.pop() (returns it)
  • Remove a specific element with L.remove(x)
    • Removes first occurrence
    • Gives error if not found

L = [2, 1, 3, 6, 3, 7, 0]

L.remove(2)   # L == [1, 3, 6, 3, 7, 0]
L.remove(3)   # L == [1, 6, 3, 7, 0]
del L[1]      # L == [1, 3, 7, 0]
L.pop()       # returns 0, L == [1, 3, 7]
  

Convert Lists ↔ Strings

  • list(s) → string to list of chars
    
    >>> s = "I<3 cs"
    >>> list(s)
    ['I', '<', '3', ' ', 'c', 's']
    
  • s.split(sep) → string to list on separator (defaults to space)
    
    >>> s.split('<')
    ['I', '3 cs']
    
  • ''.join(L) → list of strings to string
    
    >>> L = ['a', 'b', 'cde']
    >>> ''.join(L)
    'abcde'
    >>> '_'.join(L)
    'a_b_cde'
    

Other List Operations

  • sorted(L) → returns sorted list, does not mutate
  • L.sort() → sorts in place, mutates
  • L.reverse() → reverses in place, mutates

L = [9, 6, 0, 3]

sorted(L)   # [0, 3, 6, 9], L unchanged
L.sort()    # L == [0, 3, 6, 9]
L.reverse() # L == [9, 6, 3, 0]
  

→ Check Official Python Tutorial for many more!

Mutation
Aliasing
Cloning

Lists in Memory

  • Lists are mutable
  • Behave differently from immutable types
  • Each list is an object in memory
  • Variable name points to that object
  • Any variable pointing to the same object is affected
  • Key phrase: side effects

An Analogy

  • A person has attributes
    • Albert Einstein → scientist, genius
  • He is known by many names: Dr. Einstein, Professor Einstein
  • All nicknames point to the same person
    • Add a new attribute to one nickname
      • Albert Einstein → scientist, genius, curious
    • All nicknames refer to old AND new attributes
      • Dr. Einstein → scientist, genius, curious
      • Professor Einstein → scientist, genius, curious

Aliases

  • hot is an alias for warm → changing one changes the other
  • append() has a side effect

>>> a = 1
>>> b = a
>>> warm = ['red', 'yellow', 'orange']
>>> hot = warm
>>> hot.append('pink')
>>> print(warm)
['red', 'yellow', 'orange', 'pink']
  
Frames Objects Global frame a 1 b 1 warm hot list 0 "red" 1 "yellow" 2 "orange" 3 "pink"

Cloning a List

  • Create a new list and copy every element with slicing

>>> cool = ['blue', 'green', 'grey']
>>> chill = cool[:]          # clone
>>> chill.append('black')
>>> print(chill)
['blue', 'green', 'grey', 'black']
>>> print(cool)
['blue', 'green', 'grey']
  
Frames Objects Global frame cool chill list 0 "blue" 1 "green" 2 "grey" list 0 "blue" 1 "green" 2 "grey" 3 "black"

Sorting Lists

  • L.sort() → mutates the list, returns None
  • sorted(L) → does not mutate, returns a new sorted list

>>> warm = ['red', 'yellow', 'orange']
>>> sortedwarm = warm.sort()
>>> print(warm)
['orange', 'red', 'yellow']
>>> print(sortedwarm)
None
>>> cool = ['grey', 'green', 'blue']
>>> sortedcool = sorted(cool)
>>> print(cool)
['grey', 'green', 'blue']
>>> print(sortedcool)
['blue', 'green', 'grey']
  
Frames Objects Global frame warm sortedwarm None cool sortedcool list 0 "orange" 1 "red" 2 "yellow" list 0 "grey" 1 "green" 2 "blue" list 0 "blue" 1 "green" 2 "grey"

Lists of Lists of Lists of ...

  • Lists can be nested
  • Mutations can still cause side effects

>>> warm = ['yellow', 'orange']
>>> hot = ['red']
>>> brightcolors = [warm]
>>> brightcolors.append(hot)
>>> print(brightcolors)
[['yellow', 'orange'], ['red']]
>>> hot.append('pink')
>>> print(hot)
['red', 'pink']
>>> print(brightcolors)
[['yellow', 'orange'], ['red', 'pink']]
  
Frames Objects Global frame warm hot brightcolors list 0 "yellow" 1 "orange" list 0 "red" 1 "pink" list 0 1

Mutation and Iteration

⚠️ Avoid mutating a list while iterating over it!

  • Python uses an internal counter for iteration
  • Mutating changes list length but not the counter

Mutation and Iteration

Question: Removing all elements from one list (L1) that are present in another list (L2). Which code is correct?

Code #1


def remove_dups(L1, L2):
    for e in L1:
        if e in L2:
            L1.remove(e)
      

Code #2


def remove_dups(L1, L2):
    L1_copy = L1[:]
    for e in L1_copy:
        if e in L2:
            L1.remove(e)
      

5. Sets

Sets

  • Unordered collection, unique elements, written within braces
    
    >>> numbers = {1, 2, 3, 4, 1, 5}
    >>> print(numbers)
    {1, 2, 3, 4, 5}
    >>> numbers[0]
    TypeError: 'set' object is not subscriptable
      
  • Don’t be confused with dict (presented later)
    
    >>> var_a = {}
    >>> type(var_a)
    <class 'dict'>
    >>> var_b = set()
    >>> type(var_b)
    <class 'set'>
      
  • Elements are immutable, set is mutable
                    
    >>> numbers.add(6)
    >>> numbers
    {1, 2, 3, 4, 5, 6}
                    
                  

Sets

  • Support mixed types
    
    >>> numbers = {1, 2, "hello", 4}
    >>> numbers
    {1, 2, 4, 'hello'}
          
  • list not allowed
    
    >>> empty_set = {[1, 2, 4]}
    TypeError: unhashable type: 'list'
          
    • Prescisely, set items must be hashable
  • Convert list → set (removes duplicates)
    
    >>> set([1, 1, 2, 3])
    {1, 2, 3}
          

6. Dictionaries

HOW TO STORE STUDENT INFO

  • So far, can store using separate lists for every info (names, grade, course)
    • a separate list for each item
    • each list must have the same length
    • info stored across lists at same index — each index refers to info for a different person
    
    names  = ['Phong', 'Việt Anh', 'Huỳnh', 'Du']
    grade  = [3.5, 3.8, 3.2, 3.9]
    course = ['DSE2049', 'AI3054', 'AI3056', 'DSE3154']
      

How to Update/Retrieve Student Info


def get_grade(student, name_list, grade_list, course_list):
    i = name_list.index(student)
    grade = grade_list[i]
    course = course_list[i]
    return (course, grade)
  
  • Messy if there is a lot of different info to keep track of
  • Must maintain many lists and pass them as arguments
  • Must always index using integers
  • Must remember to update multiple lists consistently

A Better, Cleaner Way —
A Dictionary

  • Index items directly (keys aren’t limited to ints).
  • Keep related info in one structure—no parallel lists.

A list

Index Element
0 Elem 1
1 Elem 2
2 Elem 3
3 Elem 4
indexed by position

A dictionary

Key Value
Key 1 Val 1
Key 2 Val 2
Key 3 Val 3
Key 4 Val 4
indexed by meaningful label

A Python Dictionary

  • Stores pairs of data: key → value.

# empty dict
my_dict = {}

# a simple grade book
grades = {
  'Phong': 3.5,
  'Việt Anh': 3.8,
  'Huỳnh': 3.2,
  'Du': 3.9
}
    
Key Value
'Phong' 3.5
'Việt Anh' 3.8
'Huỳnh' 3.2
'Du' 3.9

Dictionary Lookup

  • Similar to indexing into a list
  • Looks up the key
  • Returns the value associated with the key
  • If key isn’t found → KeyError

>>> grades = {'Phong': 3.5, 'Việt Anh': 3.8, 'Huỳnh': 3.2, 'Du': 3.9}
>>> grades['Phong']
3.5
>>> grades['An']
KeyError: 'An'
  

Dictionary Operations


>>> grades = {'Phong': 3.5, 'Việt Anh': 3.8, 'Huỳnh': 3.2, 'Du': 3.9}
  
Add an entry

>>> grades['An'] = 4.0
  
Test if a key is in the dictionary

>>> 'Phong' in grades
True
>>> 'Long' in grades
False
  
Delete an entry

>>> del(grades['Du'])
  

Dictionary Operations


>>> grades = {'Phong': 3.5, 'Việt Anh': 3.8, 'Huỳnh': 3.2, 'Du': 3.9}
Iterate over keys

>>> grades.keys()    
dict_keys(['Phong', 'Việt Anh', 'Huỳnh', 'Du'])
Iterate over values

>>> grades.values()
dict_values([3.5, 3.8, 3.2, 3.9])
  

🆕 Python3.7+: Iteration order matches insertion order

Dictionary Keys and Values

  • Values
    • Can be any type (immutable or mutable)
    • Values can be duplicates
    • Values can even be lists or other dictionaries
  • Keys
    • Must be unique
    • Must be an immutable type: int, float, string, tuple, bool
      • Prescisely, the key must be hashable
    • Be careful when using float as a key

list vs dict

Aspect list dict
Structure ordered sequence of elements matches “keys” to “values”
Lookup Look up elements by an integer index Look up one item by another item
Order Indices have an order Python 3.7+: insertion order preserved
Index/Key Index is an integer Key can be any immutable type