Lecture 7: More Conditionals, Loops and Lists¶
Welcome back! Today we'll learn more about conditionals, loops and lists. By the end of this lecture, you will be able to:
- use ternary operator to contract
if-elsestatements - use
whileloops for conditional repetition - use list comprehensions for concise list creation
- list slicing and other list methods
Conditional Expressions (Ternary Operator)¶
Last week, we saw conditionals with if-elif-else that span multiple lines. But Python also provides a conditional expression (also called a ternary operator) that contracts the if-else structure into a single line:
value = result_if_true if condition else result_if_false
This is equivalent to:
if condition:
value = result_if_true
else:
value = result_if_false
For example, to check if a number is even or odd:
number = 7
if number % 2 == 0:
parity = "even"
else:
parity = "odd"
print(f"{number} is {parity}")
7 is odd
Instead of using the if-else that spans multiple lines, we can use the conditional expression:
# Using conditional expression (one line)
parity = "even" if number % 2 == 0 else "odd"
print(f"{number} is {parity}")
7 is odd
When to Use Conditional Expressions¶
Conditional expressions are great for simple, binary choices where you're just picking between two values. They make your code more concise.
Here are some examples of good uses:
# Assigning a value based on a simple condition
age = 15
ticket_price = 10 if age >= 18 else 5
print(f"Ticket price: ${ticket_price}")
Ticket price: $5
# Choosing a message to display
temperature = 72
weather_advice = "Enjoy the weather!" if temperature < 80 else "Stay cool!"
print(weather_advice)
Enjoy the weather!
# Setting a default value
username = ""
name = username if username != "" else "Guest"
print(f"Hello, {name}!")
Hello, Guest!
When NOT to Use Conditional Expressions¶
Avoid conditional expressions when:
- The logic is complex (multiple conditions)
- You need to execute multiple statements in each branch
- Readability would suffer
Bad example usage:
yarn_weight = 3 # 1-7 scale (1=lace, 4=worsted, 7=jumbo)
# Don't do this! Overly long and unreadable
yarn_type = "jumbo" if yarn_weight > 6 else "bulky" if yarn_weight > 4 else "medium" if yarn_weight > 2 else "fine"
print(yarn_type)
medium
# BETTER: Use if-elif-else for multiple conditions
if yarn_weight <= 2:
yarn_type = "fine"
elif yarn_weight <= 4:
yarn_type = "medium"
elif yarn_weight <= 6:
yarn_type = "bulky"
else:
yarn_type = "jumbo"
print(yarn_type)
medium
Conditional Expressions in Context¶
Conditional expressions are especially useful when combined with other operations:
# In list operations
numbers = [1, 2, 3, 4, 5]
# Double even numbers, keep odd numbers as is
result = []
for n in numbers:
result.append(n * 2 if n % 2 == 0 else n)
print(result) # [1, 4, 3, 8, 5]
[1, 4, 3, 8, 5]
# In functions for concise return value construction
def grade(score):
return "Satisfactory" if score > 0 else "Not Yet"
print(grade(1.0))
Satisfactory
# In string formatting
items = 3
message = f"You have {items} {'item' if items == 1 else 'items'}"
print(message)
You have 3 items
In summary, conditional expressions are powerful for simple choices, but don't sacrifice readability for brevity. When in doubt, use a full if-else statement!
The while Loop¶
A while loop repeats as long as a condition is true. Unlike for loops where you know the number of iterations, while loops continue until something changes and causes the condition to be no longer true.
Syntax:
while condition:
# code to repeat
# (make sure to eventually make condition False!)
Think of it like: "While I still have yarn, keep knitting."
# Count down from 5
count = 5
while count > 0:
print(f"Count: {count}")
count = count - 1 # Decrease count (important!)
print("Done!")
Count: 5 Count: 4 Count: 3 Count: 2 Count: 1 Done!
while loops are where most infinite loop are caused. So if you run into an infinite loop, or a piece of code that runs for too long that it feels unusual, that's what to look for and debug.
# DON'T DO THIS - infinite loop!
count = 5
while count > 0:
print(count)
# count -= 1
When to Use while vs for¶
Use for when:
- You know how many iterations you need
- You're iterating over a sequence (list, range, etc.)
Use while when:
- You don't know how many iterations you'll need
- You're waiting for a specific condition
- You're processing input until a sentinel value
# Example: keep doubling until we exceed 1000
value = 1
iterations = 0
while value <= 1000:
print(f"Iteration {iterations}: value = {value}")
value = value * 2
iterations = iterations + 1
print(f"\nFinal value: {value} (after {iterations} iterations)")
Iteration 0: value = 1 Iteration 1: value = 2 Iteration 2: value = 4 Iteration 3: value = 8 Iteration 4: value = 16 Iteration 5: value = 32 Iteration 6: value = 64 Iteration 7: value = 128 Iteration 8: value = 256 Iteration 9: value = 512 Final value: 1024 (after 10 iterations)
# Example: keep printing until the input from user is empty
user_input = input("Please enter what you want to say and ends with empty input:")
while user_input != "":
print(user_input)
user_input = input()
Welcome to COMP116 !
Note that here input(prompt) is being used to request input from user and this function only returns strings even if we enter a number. The prompt is an optional argument that can be skipped.
List Comprehensions¶
List comprehensions provide a concise way to create lists. They're a more compact alternative to building lists with loops.
Basic syntax:
[expression for item in sequence]
This is equivalent to:
result = []
for item in sequence:
result.append(expression)
# Traditional way: loop with append
squares = []
for i in range(10):
squares.append(i ** 2)
print(f"With loop: {squares}")
With loop: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# List comprehension: more concise!
squares = [i ** 2 for i in range(10)]
print(f"With comprehension: {squares}")
With comprehension: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Here are some more examples:
# More examples
evens = [i * 2 for i in range(10)]
print(f"Evens: {evens}")
Evens: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Convert a list of measurements from inches to cm
inches = [4, 8, 12, 16]
centimeters = [inch * 2.54 for inch in inches]
print(f"Centimeters: {centimeters}")
Centimeters: [10.16, 20.32, 30.48, 40.64]
List Comprehensions with Conditionals¶
You can add an if clause to filter which items are included:
[expression for item in sequence if condition]
This is equivalent to:
result = []
for item in sequence:
if condition:
result.append(expression)
# Get only the even numbers from 0-19
evens = [i for i in range(20) if i % 2 == 0]
print(f"Evens: {evens}")
Evens: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Get only the large measurements
measurements = [12, 5, 8, 15, 3, 20, 7]
large_measurements = [m for m in measurements if m >= 10]
print(f"Large measurements: {large_measurements}")
Large measurements: [12, 15, 20]
Poll Question: What Does This List Comprehension Produce?¶
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = [x * 2 if x % 3 == 0 else x for x in numbers if x % 2 == 0]
print(result)
Take a moment to think about it before running the cell!
Explanations
Let's break down what's happening:
[x * 2 if x % 3 == 0 else x # What to do with each x
for x in numbers # Iterate through numbers
if x % 2 == 0] # Only include if even
Step by step:
if x % 2 == 0at the end filters to only even numbers:[2, 4, 6, 8, 10]- For each of these, check
if x % 3 == 0(divisible by 3):- 2: not divisible by 3 → keep as 2
- 4: not divisible by 3 → keep as 4
- 6: divisible by 3! → double it to 12
- 8: not divisible by 3 → keep as 8
- 10: not divisible by 3 → keep as 10
Was this piece of code easy to read at a glance? Likely not easy compared to the code below that uses a traditional for loop with appropriate amount of comments:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = []
for x in numbers:
if x % 2 == 0: # Only process even numbers
if x % 3 == 0: # If also divisible by 3
result.append(x * 2) # Double it
else:
result.append(x) # Keep it as is
print(result)
When to Use List Comprehensions¶
List comprehensions are great when:
- Creating a new list from an existing sequence
- The transformation is simple (one line)
- You want concise, readable code
Use traditional loops when:
- The logic is complex (multiple lines)
- You need to do more than build a list
- Readability would suffer
List Slicing¶
Last time, we saw list indexing with the [] operator. This only selects one item at a time.
Today we'll learn how to extract portions of a list, which is useful when you want to work with only part of your data.
list[start:stop] or list[start:stop:step].
Note how this exactly matches the range() function's parameter, and indeed they are mirrorring each other. So start is inclusive, stop is exclusive, and step can be negative.
# A list of yarn colors in our inventory
colors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange']
# Get the second and third colors (indices 1 and 2)
print(colors[1:3]) # ['blue', 'green']
['blue', 'green']
Similar to range(stop), you can omit the first index, which means that the slice starts at the beginning:
# Get the first 3 colors
print(colors[:3]) # ['red', 'blue', 'green']
['red', 'blue', 'green']
But slightly different from range(), in list slicing you can omit the second index and keep only the first index. This means the slice goes to the end starting from the first index:
# Get all colors starting from index 3
print(colors[3:]) # ['yellow', 'purple', 'orange']
['yellow', 'purple', 'orange']
Copying a List¶
If you omit both indices, you get a copy of the entire list:
# Create a backup of our color list
colors_backup = colors[:]
print(colors_backup) # ['red', 'blue', 'green', 'yellow', 'purple', 'orange']
['red', 'blue', 'green', 'yellow', 'purple', 'orange']
This creates a NEW list (a copy), not just another name for the same list! For example, the following is creating not a copy of the list but just another variable name for the same list:
colors_backup = colors # these two are the SAME, no new list is being created
Another way to copy a list is with the list() function (recall that without any argument this creates an empty list):
colors_copy = list(colors)
print(colors_copy)
['red', 'blue', 'green', 'yellow', 'purple', 'orange']
Or calling the method copy on the list:
colors_copy = colors.copy()
print(colors_copy)
['red', 'blue', 'green', 'yellow', 'purple', 'orange']
Slicing with Step¶
You can also specify a step value to skip elements:
stitch_pattern = ['k', 'p', 'k', 'p', 'k', 'p', 'k', 'p']
# Get every other element (step=2)
print(stitch_pattern[::2]) # ['k', 'k', 'k', 'k'] - all knits
print(stitch_pattern[1::2]) # ['p', 'p', 'p', 'p'] - all purls
['k', 'k', 'k', 'k'] ['p', 'p', 'p', 'p']
And since step can be negative, you can reverse a list with negative step -1:
print(stitch_pattern[::-1]) # ['p', 'k', 'p', 'k', 'p', 'k', 'p', 'k']
['p', 'k', 'p', 'k', 'p', 'k', 'p', 'k']
More List Methods¶
We've already seen .append() for adding elements. Python provides several other useful list methods:
extend() - Add Multiple Elements¶
While .append() adds a single element, .extend() adds all elements from another list:
# We have some yarn colors
my_yarns = ['red', 'blue', 'green']
# Difference between append and extend:
test_append = my_yarns.copy()
test_append.append(['yellow', 'purple']) # Adds the list itself as a single element
print(f"After append: {test_append}")
test_extend = my_yarns.copy()
test_extend.extend(['yellow', 'purple']) # Adds each element individually
print(f"After extend: {test_extend}")
After append: ['red', 'blue', 'green', ['yellow', 'purple']] After extend: ['red', 'blue', 'green', 'yellow', 'purple']
pop() - Remove and Return an Element¶
The .pop() method removes an element at a specific index and returns it. If no index is given, it removes the last element:
needles = ["US 2", "US 4", "US 6", "US 8", "US 10"]
# Remove the needle at index 1 (US 4)
removed = needles.pop(1)
print(f"Removed: {removed}")
print(f"Remaining needles: {needles}")
# Remove the last needle
last = needles.pop()
print(f"Removed last: {last}")
print(f"Remaining needles: {needles}")
Removed: US 4 Remaining needles: ['US 2', 'US 6', 'US 8', 'US 10'] Removed last: US 10 Remaining needles: ['US 2', 'US 6', 'US 8']
remove() - Remove by Value¶
If you know the value you want to remove (but not its index), use .remove():
project_types = ['scarf', 'hat', 'sweater', 'socks', 'hat']
# Remove the first occurrence of 'hat'
project_types.remove('hat')
print(project_types) # ['scarf', 'sweater', 'socks', 'hat']
['scarf', 'sweater', 'socks', 'hat']
Important difference:
.pop()returns the removed value.remove()returnsNone(nothing)
If the element doesn't exist, .remove() raises a ValueError:
project_types = ['scarf', 'hat', 'sweater']
# This will cause an error:
project_types.remove('blanket') # ValueError: 'blanket' is not in list
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[35], line 4 1 project_types = ['scarf', 'hat', 'sweater'] 3 # This will cause an error: ----> 4 project_types.remove('blanket') # ValueError: 'blanket' is not in list ValueError: list.remove(x): x not in list
# Better to check first:
if 'blanket' in project_types:
project_types.remove('blanket')
else:
print("Blanket not in list!")
Blanket not in list!
2nd Poll on List Methods¶
What do you think the following code is going to print?
colors = ['red', 'blue', 'green']
colors_copy = colors
last_color = colors.pop()
colors_copy.remove(last_color)
print(colors_copy)
Explanations
We can use Python Tutor to visualize this piece of code and see what is happening.
The tricky part is to that colors_copy is actually not a copy of colors, but another variable pointing to the exact same list. So colors.pop() is going to pop the last item, which is 'green', and then both colors and colors_copy now point to the list ['red', 'blue']. So now if we run colors_copy.remove(last_color), this is going to cause an error because last_color which is 'green' is already removed by pop().
A follow-up question: how to make this print ['red', 'blue'] as expected?
Sorting Lists¶
Python provides the sorted() function to sort elements in a list. Important: sorted() returns a NEW sorted list and does NOT modify the original:
yarn_weights = ['worsted', 'fingering', 'bulky', 'dk', 'lace']
# sorted() creates a new sorted list
sorted_weights = sorted(yarn_weights)
print(f"Sorted: {sorted_weights}")
print(f"Original: {yarn_weights}") # Original unchanged!
Sorted: ['bulky', 'dk', 'fingering', 'lace', 'worsted'] Original: ['worsted', 'fingering', 'bulky', 'dk', 'lace']
Sorting Numbers¶
stitch_counts = [45, 23, 67, 12, 89, 34]
# Sort in ascending order (smallest to largest)
print(sorted(stitch_counts))
# Sort in descending order (largest to smallest)
print(sorted(stitch_counts, reverse=True))
[12, 23, 34, 45, 67, 89] [89, 67, 45, 34, 23, 12]
The .sort() Method¶
There's also a .sort() method that sorts the list in place (modifies the original):
needle_sizes = [8, 4, 10, 2, 6]
# .sort() modifies the list directly
needle_sizes.sort()
print(needle_sizes) # [2, 4, 6, 8, 10]
# Note: .sort() returns None, not the sorted list!
result = needle_sizes.sort()
print(f"Return value: {result}") # None
[2, 4, 6, 8, 10] Return value: None
When to use which:
- Use
sorted()when you want to keep the original list unchanged - Use
.sort()when you want to modify the list in place and don't need a copy
Practice Problems¶
Try these to solidify your understanding!
Problem 1: Generate Row Numbers¶
Write code that prints "Row 1", "Row 2", ... "Row 20"
# TODO: your code here
Problem 2: Create a Color List¶
Use a list comprehension to create a list of 10 elements, alternating between "blue" and "green".
Hint: use the modulo operator to determine which color!
# TODO: your code here
# Expected: ["blue", "green", "blue", "green", ...]
Problem 3: While Loop Counter¶
Write a while loop that starts at 1 and keeps doubling until the value exceeds 100. Count how many iterations it takes.
# TODO: your code here
Summary¶
In this lecture, we learned:
- conditional expressions (ternary operator)
whileloops repeat until a condition becomes false- List comprehensions create lists concisely
- Filtering with conditional list comprehensions
- List slicing and manipulation methods
Some of these are quite advanced so don't worry if it doesn't come as easily to you yet (they are also not needed for HW2). When you encounter these in other contexts and practice more, they will become more familiar!