Unit 4 - Notes

INT108

Unit 4: String; Lists; Tuples and Dictionaries

Part 1: Strings

1. String as a Compound Data Type

A string is a sequence of characters. It is considered a compound data type because it is made up of smaller pieces (characters). In Python, strings are immutable, meaning once defined, they cannot be changed in place.

PYTHON
name = "Python"
# 'P', 'y', 't', 'h', 'o', 'n' are the constituent parts.

2. Length

The len() function returns the number of characters in a string.

PYTHON
fruit = "banana"
length = len(fruit) 
print(length) # Output: 6

3. String Traversal

Traversal involves processing a string one character at a time.

Using a for loop (preferred):

PYTHON
fruit = "Apple"
for char in fruit:
    print(char)

Using a while loop:

PYTHON
index = 0
while index < len(fruit):
    letter = fruit[index]
    print(letter)
    index += 1

4. String Slices

A segment of a string is called a slice. The syntax is string[start:stop:step].

  • start: Beginning index (inclusive).
  • stop: Ending index (exclusive).
  • step: Stride (default is 1).

PYTHON
s = "Monty Python"
print(s[0:5])   # Output: Monty
print(s[6:12])  # Output: Python
print(s[:5])    # Output: Monty (starts at beginning)
print(s[6:])    # Output: Python (goes to end)
print(s[:])     # Output: Monty Python (copy of whole string)
print(s[::-1])  # Output: nohtyP ytnoM (reverses string)

5. Comparison

Strings can be compared using standard relational operators (==, <, >, etc.). Python compares strings lexicographically (dictionary order) based on ASCII/Unicode values.

  • Uppercase letters come before lowercase letters in ASCII.

PYTHON
word = "banana"
if word == "banana":
    print("Match")

if "apple" < "banana":
    print("apple comes before banana")

6. Find Function

The find() method searches for a substring within a string. It returns the index of the first occurrence or -1 if not found.

PYTHON
word = "banana"
index = word.find("a")
print(index)        # Output: 1
index_na = word.find("na")
print(index_na)     # Output: 2

7. Looping and Counting

A common pattern in string processing is looping through the string to count specific occurrences.

PYTHON
word = "banana"
count = 0
for letter in word:
    if letter == 'a':
        count = count + 1
print(count) # Output: 3


Part 2: Lists

1. List Values and Accessing Elements

A list is an ordered sequence of values. The values can be of any type (integers, strings, other lists). Lists are mutable.

Accessing Elements: Uses zero-based indexing.

PYTHON
numbers = [10, 20, 30, 40]
print(numbers[0])    # Output: 10
print(numbers[-1])   # Output: 40 (last element)

2. Membership

The in and not in operators check if a value exists in a list. Returns a Boolean.

PYTHON
cheeses = ['Cheddar', 'Edam', 'Gouda']
print('Edam' in cheeses)      # Output: True
print('Brie' in cheeses)      # Output: False

3. List Operations

  • Concatenation (+): Joins two lists.
  • *Repetition ():** Repeats a list a given number of times.

PYTHON
a = [1, 2, 3]
b = [4, 5, 6]
c = a + b       # [1, 2, 3, 4, 5, 6]
d = [0] * 4     # [0, 0, 0, 0]

4. List Slices

Works identically to string slicing but returns a new list. Slices can also be used to modify sub-parts of a list (because lists are mutable).

PYTHON
t = ['a', 'b', 'c', 'd', 'e']
print(t[1:3])       # Output: ['b', 'c']
t[1:3] = ['x', 'y'] # Modifies the list
print(t)            # Output: ['a', 'x', 'y', 'd', 'e']

5. Deletion

There are three main ways to delete elements:

  1. pop(index): Removes and returns the element at the index. If no index is provided, removes the last one.
  2. del: Removes an element by index (does not return it). Can also delete slices.
  3. remove(value): Removes the first matching value.

PYTHON
t = ['a', 'b', 'c']
x = t.pop(1)    # x is 'b', t is ['a', 'c']

t = ['a', 'b', 'c']
del t[1]        # t is ['a', 'c']

t = ['a', 'b', 'c']
t.remove('b')   # t is ['a', 'c']

6. List and For Loops

Iterating over elements:

PYTHON
for num in [1, 2, 3]:
    print(num)

Iterating over indices (to update elements):

PYTHON
numbers = [1, 2, 3]
for i in range(len(numbers)):
    numbers[i] = numbers[i] * 2
# numbers is now [2, 4, 6]

7. List Parameters

When a list is passed to a function, the function gets a reference to the list. Modifications inside the function affect the original list.

PYTHON
def delete_head(t):
    del t[0]

letters = ['a', 'b', 'c']
delete_head(letters)
print(letters) # Output: ['b', 'c']

8. Nested Lists

A list can contain other lists as elements. This is often used to represent matrices.

PYTHON
nested = ["hello", 2.0, [10, 20]]
print(nested[2])    # Output: [10, 20]
print(nested[2][1]) # Output: 20


Part 3: Tuples

1. Mutability and Tuples

A tuple is a sequence of values much like a list, but tuples are immutable. Once created, you cannot modify elements. They are defined using parentheses ().

PYTHON
t = ('a', 'b', 'c', 'd', 'e')
# t[0] = 'z'  <- This raises a TypeError

Note: A tuple with a single element must include a trailing comma: t1 = ('a',).

2. Tuple Assignment

Python allows a tuple of variables on the left side of an assignment to be assigned values from a tuple on the right side. This implies simultaneous assignment.

PYTHON
# Basic assignment
m = ('have', 'fun')
x, y = m
# x is 'have', y is 'fun'

# Swapping values (Idiomatic Python)
a = 10
b = 20
a, b = b, a  
# a is 20, b is 10

3. Tuples as Return Values

Functions can strictly return only one value. However, by returning a tuple, a function can effectively return multiple values.

PYTHON
def min_max(t):
    return min(t), max(t) # Returns a tuple

bounds = min_max([10, 2, 5, 90])
print(bounds) # Output: (2, 90)


Part 4: Dictionaries

1. Basics

A dictionary is a mapping type (hash map) containing key-value pairs. Keys must be unique and immutable types (strings, numbers, tuples). Dictionaries are defined using curly braces {}.

PYTHON
eng2sp = {'one': 'uno', 'two': 'dos', 'three': 'tres'}
print(eng2sp['two']) # Output: dos

2. Operations

  • Access: dict[key]
  • Insertion/Update: dict[key] = value
  • Deletion: del dict[key]
  • Length: len(dict) returns number of pairs.

PYTHON
inventory = {'apples': 430, 'bananas': 312}
inventory['pears'] = 200   # Insert
inventory['bananas'] += 10 # Update
del inventory['apples']    # Delete

3. Dictionary Methods

  • keys(): Returns a view object of all keys.
  • values(): Returns a view object of all values.
  • items(): Returns a view object of (key, value) tuples.
  • get(key, default): Returns value if key exists, else returns default (prevents KeyErrors).

PYTHON
counts = {'chuck': 1, 'annie': 42, 'jan': 100}
print(counts.get('jan', 0))   # Output: 100
print(counts.get('tim', 0))   # Output: 0

4. Sparse Matrices using Dictionaries

A matrix with many zero values is called "sparse." Storing every zero in a list of lists is inefficient. A dictionary can store only the non-zero elements using tuples as keys representing coordinates (row, col).

PYTHON
# List of Lists representation (Inefficient for sparse data)
# matrix = [[0, 0, 0, 1], [0, 0, 0, 0], [0, 2, 0, 0]]

# Dictionary representation
matrix_dict = {(0, 3): 1, (2, 1): 2}

# Accessing an element
# Using .get() allows us to return 0 if the coordinate isn't in the dict
print(matrix_dict.get((0, 3), 0)) # Output: 1
print(matrix_dict.get((1, 1), 0)) # Output: 0


Part 5: Aliasing and Copying

This concept applies to mutable objects (Lists and Dictionaries).

1. Aliasing

Aliasing occurs when two variables refer to the same object in memory. Changes made through one variable affect the other.

PYTHON
a = [1, 2, 3]
b = a         # b is an alias for a
b[0] = 99
print(a)      # Output: [99, 2, 3] - a is changed!

Here, a and b point to the exact same list object ID.

2. Copying

To modify a list without affecting the original, you must create a copy.

Shallow Copy (Slicing):
The easiest way to copy a list is using the slice operator with no arguments.

PYTHON
a = [1, 2, 3]
b = a[:]      # b is a copy of a
b[0] = 99
print(a)      # Output: [1, 2, 3] - a is unchanged
print(b)      # Output: [99, 2, 3]

Deep Copy:
If a list contains other lists (nested), a shallow copy only copies the references to the inner lists. To copy everything recursively, use the copy module.

PYTHON
import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)