Lists & Conditional Control Flow

Session 2 — Lists · If Statements

SEC1302 · Python for Information Security

>>> ports = [22, 80, 443]
>>> if 22 in ports:
... print("SSH exposed")
SSH exposed

Agenda

Lists

  1. What is a list (Accessing elements · Index positions · Using individual values)
  1. Changing, adding, removing (Modifying · Adding · Removing)
  1. Organizing a list (sort() · sorted() · Reverse order · Length)
  1. Avoiding index errors
  1. Looping through a list (Closer look · More work in a loop · After a loop · Indentation errors)
  1. Numerical lists (range() · range() to list · Simple statistics · List comprehensions)
  1. Working with part of a list (Slicing · Looping through a slice · Copying)
  1. Tuples (Defining · Looping · Writing over a tuple)

If Statements

  1. A simple example
  1. Conditional tests (Equality · Ignoring case · Inequality · Numerical comparisons · Multiple conditions, in / not in)
  1. if statements (Simple if · if-else · if-elif-else · Multiple elif · Omitting else · Testing multiple conditions)
  1. Using if statements with lists (Special items · Non-empty list · Multiple lists)

Exercises · Chapter summaries

What learners will be able to do after this session

Create, index, modify, sort and slice lists; explain the difference between a list and a tuple.

Iterate over lists with for loops and range(); build lists with list comprehensions.

Write conditional tests (==, !=, <, >, and, or, in, not in) and if / if-else / if-elif-else chains.

Combine lists and if statements to implement simple security checks (allow-lists, block-lists, input normalisation).

Mapped to CLO1 — apply Python to solve basic programming problems (C3-P2).

Lists

What Is a List?

  • A list is an ordered collection of items, written in square brackets [ ] with items separated by commas.
  • A list can hold any type of value, and items do not need to be related; names are usually plural (bicycles, ports, users).
  • Lists are mutable: items can be changed after creation.
bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles)
['trek', 'cannondale', 'redline', 'specialized']

Security examples of lists

  • open ports [22, 80, 443]
  • banned IP addresses
  • usernames from a log file

Accessing elements

bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles[0])
print(bicycles[0].title())
message = f"My first bicycle was a {bicycles[0].title()}."
print(message)
trek
Trek
My first bicycle was a Trek.

Explanation

  • Write the list name followed by the index in square brackets.
  • The result is the item itself (here a str), so string methods from Chapter 2 such as title() apply directly.
  • Individual items can be used inside f-strings like any other variable.

Index positions start at 0, not 1

trek

positive: 0 | negative: -4

cannondale

positive: 1 | negative: -3

redline

positive: 2 | negative: -2

specialized

positive: 3 | negative: -1

bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles[1])
print(bicycles[3])
print(bicycles[-1])
cannondale
specialized
specialized
  • The second item has index 1; the fourth item has index 3.
  • Negative indexes count from the end: -1 is the last item, -2 the second-to-last.
  • bicycles[-1] works for a list of any length; useful for "the most recent log entry".

Modifying elements in a list

motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
motorcycles[0] = 'ducati'
print(motorcycles)
['honda', 'yamaha', 'suzuki']
['ducati', 'yamaha', 'suzuki']

Assigning to list[index] replaces the item at that position; other items are unchanged.

Contrast

Strings are immutable (Chapter 2), so "honda"[0] = "H" raises TypeError; lists are mutable.

Adding elements to a list

append() adds to the end

motorcycles = []
motorcycles.append('honda')
motorcycles.append('yamaha')
motorcycles.append('suzuki')
print(motorcycles)
['honda', 'yamaha', 'suzuki']

insert() adds at a position

motorcycles = ['honda', 'yamaha', 'suzuki']
motorcycles.insert(0, 'ducati')
print(motorcycles)
['ducati', 'honda', 'yamaha', 'suzuki']

Removing elements — del and pop()

del by position

motorcycles = ['honda', 'yamaha', 'suzuki']
del motorcycles[1]
print(motorcycles)
['honda', 'suzuki']

pop() removes the last item and returns it

motorcycles = ['honda', 'yamaha', 'suzuki']
popped_motorcycle = motorcycles.pop()
print(motorcycles)
print(popped_motorcycle)
['honda', 'yamaha']
suzuki

pop(index) removes any position

motorcycles = ['honda', 'yamaha', 'suzuki']
first_owned = motorcycles.pop(0)
print(f"The first motorcycle I owned was a {first_owned.title()}.")
The first motorcycle I owned was a Honda.

Visual: "stack" diagram for pop() — the top of the stack is the end of the list.

Removing by value — remove(), and which method to choose

motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati']
too_expensive = 'ducati'
motorcycles.remove(too_expensive)
print(motorcycles)
print(f"\nA {too_expensive.title()} is too expensive for me.")
['honda', 'yamaha', 'suzuki']
A Ducati is too expensive for me.

Decision table

Rule of thumb (coursebook): use del when the item is no longer needed; use pop() when the item is still needed.

Sorting a list permanently with sort()

cars = ['bmw', 'audi', 'toyota', 'subaru']
cars.sort()
print(cars)
cars.sort(reverse=True)
print(cars)
['audi', 'bmw', 'subaru', 'toyota']
['toyota', 'subaru', 'bmw', 'audi']

Notes

  • sort() changes the list in place and returns None (a common bug: cars = cars.sort() sets cars to None).
  • Python's sort is guaranteed to be stable: items that compare equal keep their original order.

Sorting a list temporarily with sorted()

cars = ['bmw', 'audi', 'toyota', 'subaru']
print("Here is the original list:")
print(cars)
print("\nHere is the sorted list:")
print(sorted(cars))
print("\nHere is the original list again:")
print(cars)
Here is the original list:
['bmw', 'audi', 'toyota', 'subaru']

Here is the sorted list:
['audi', 'bmw', 'subaru', 'toyota']

Here is the original list again:
['bmw', 'audi', 'toyota', 'subaru']

Uppercase letters sort before lowercase — use key=

cars = ['bmw', 'Audi', 'toyota', 'Subaru']
print(sorted(cars))
print(sorted(cars, key=str.lower))
['Audi', 'Subaru', 'bmw', 'toyota']
['Audi', 'bmw', 'Subaru', 'toyota']

Strings compare by Unicode code point, so 'S' (83) comes before 'b' (98). The key function converts each item before comparison without changing the stored values.

Security example — sorting IP addresses correctly

import ipaddress
ips = ['10.0.0.2', '10.0.0.10', '9.1.1.1']
print(sorted(ips))
print(sorted(ips, key=ipaddress.ip_address))
['10.0.0.10', '10.0.0.2', '9.1.1.1']
['9.1.1.1', '10.0.0.2', '10.0.0.10']

Key message: text sorting puts "10.0.0.10" before "10.0.0.2"; analysts should sort IPs as addresses.

Printing in reverse order · Finding the length

cars = ['bmw', 'audi', 'toyota', 'subaru']
cars.reverse()
print(cars)
print(len(cars))
['subaru', 'toyota', 'audi', 'bmw']
4

Notes

  • reverse() flips the current order in place (not alphabetical); calling it again restores the original order.
  • len() counts items starting from 1; the last valid index is len(lst) - 1.
  • Related built-in: reversed(lst) returns an iterator without changing the list.

Avoiding index errors when working with lists

motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles[3])
Traceback (most recent call last):
  File "motorcycles.py", line 2, in <module>
    print(motorcycles[3])
IndexError: list index out of range

A list of 3 items has indexes 0, 1, 2; index 3 asks for a fourth item.

Other cases

  • motorcycles = [] then motorcycles[-1] → IndexError: list index out of range
  • [].pop() → IndexError: pop from empty list

Why index errors matter in security

Looping through an entire list

magicians = ['alice', 'david', 'carolina']
for magician in magicians:
    print(magician)
alice
david
carolina

Flow

The for statement iterates over the items of any sequence in order; no index variable is needed.

A closer look at looping

Loop variable naming

The loop variable name is chosen by the programmer; singular/plural pairs make code readable:

for cat in cats:
for dog in dogs:
for item in list_of_items:
for port in open_ports:

The loop runs once per item, whether the list has 3 items or 1,000,000.

C++ classic loop vs Python

// C++
for (int i = 0; i < n; i++) { cout << arr[i]; }

# Python
for item in items:
    print(item)

When the index is also needed, use enumerate():

for i, magician in enumerate(magicians):
    print(i, magician)
0 alice
1 david
2 carolina

More work within a loop · Doing something after a loop

magicians = ['alice', 'david', 'carolina']
for magician in magicians:
    print(f"{magician.title()}, that was a great trick!")
    print(f"I can't wait to see your next trick, {magician.title()}.\n")
print("Thank you, everyone. That was a great magic show!")
Alice, that was a great trick!
I can't wait to see your next trick, Alice.

David, that was a great trick!
I can't wait to see your next trick, David.

Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.

Thank you, everyone. That was a great magic show!

Key point

Indented lines run once per item; the unindented final line runs once after the loop.

Security parallel

A scanner reports each host inside the loop, then prints one summary line after the loop.

Avoiding indentation errors

Using the range() function

for value in range(1, 5):
    print(value)
1
2
3
4

Explanation

  • range(start, stop) stops BEFORE stop ("off-by-one" behavior); range(1, 6) is needed to reach 5.
  • range(6) starts from 0 → 0, 1, 2, 3, 4, 5.
  • A range object stores only start, stop and step, so it uses the same small amount of memory regardless of its length.

Using range() to make a list of numbers

numbers = list(range(1, 6))
print(numbers)
even_numbers = list(range(2, 11, 2))
print(even_numbers)
squares = []
for value in range(1, 11):
    squares.append(value ** 2)
print(squares)
[1, 2, 3, 4, 5]
[2, 4, 6, 8, 10]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Security example: well-known port range

well_known_ports = range(0, 1024)
print(len(well_known_ports), well_known_ports[0], well_known_ports[-1])
1024 0 1023

Sources: docs.python.org — range function
RFC 6335 (port number ranges: System Ports 0–1023)

Simple statistics with a list of numbers

digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
print(min(digits))
print(max(digits))
print(sum(digits))
0
9
45

Security example — failed logins per hour

failed_logins = [3, 0, 1, 42, 2]
print(max(failed_logins), sum(failed_logins) / len(failed_logins))
42 9.6

The peak (42) stands out against the mean (9.6) — the basis of simple threshold alerts.

Optional: the statistics module provides mean(), median(), stdev().

List comprehensions

squares = [value ** 2 for value in range(1, 11)]
print(squares)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Anatomy: [ expression for variable in iterable ] → [ value**2 | for value in range(1, 11) ]

Comparison: 4-line loop with append() (previous slide) vs 1-line comprehension — same result.

Extension (with a condition)

ports = [21, 22, 80, 443, 3389]
risky = [p for p in ports if p in (21, 23, 3389)]
print(risky)
[21, 3389]

Guideline: use a comprehension when it stays readable on one line; otherwise use a normal loop.

Slicing a list

players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[0:3])
print(players[1:4])
print(players[:4])
print(players[2:])
print(players[-3:])
print(players[::2])
['charles', 'martina', 'michael']
['martina', 'michael', 'florence']
['charles', 'martina', 'michael', 'florence']
['michael', 'florence', 'eli']
['michael', 'florence', 'eli']
['charles', 'michael', 'eli']

Notes

Index ruler: start (included) and stop (excluded).

Looping through a slice

players = ['charles', 'martina', 'michael', 'florence', 'eli']
print("Here are the first three players on my team:")
for player in players[:3]:
    print(player.title())
Here are the first three players on my team:
Charles
Martina
Michael

Security parallel

Show the top 5 offending IP addresses after sorting by number of alerts:

for ip in sorted_ips[:5]:
    ...

Note: "last N lines of a log" is lines[-N:], similar to the Unix tail command.

Copying a list — a copy is not the same as a second name

Correct copy with a slice

my_foods = ['pizza', 'falafel', 'carrot cake']
friend_foods = my_foods[:]
my_foods.append('cannoli')
friend_foods.append('ice cream')
print(my_foods)
print(friend_foods)
['pizza', 'falafel', 'carrot cake', 'cannoli']
['pizza', 'falafel', 'carrot cake', 'ice cream']

This does NOT copy

my_foods = ['pizza', 'falafel', 'carrot cake']
friend_foods = my_foods
my_foods.append('cannoli')
friend_foods.append('ice cream')
print(my_foods)
['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']

Memory diagram: two names → one list object (alias) vs two names → two list objects (copy). Link back to Chapter 2 "variables are labels".

Advanced note — shallow vs deep copy

import copy
rules = [['22', 'deny'], ['80', 'allow']]
shallow = rules[:]
shallow[0][1] = 'allow'
print(rules)
[['22', 'allow'], ['80', 'allow']]

A slice copies only the outer list; nested lists are shared. copy.deepcopy(rules) copies them too — important when testing changes to a firewall rule set without altering the original.

Defining a tuple — an immutable list

dimensions = (200, 50)
print(dimensions[0])
print(dimensions[1])
dimensions[0] = 250
200
50
Traceback (most recent call last):
  File "dimensions.py", line 4, in <module>
    dimensions[0] = 250
TypeError: 'tuple' object does not support item assignment

Note: a tuple is defined by the comma, not the parentheses

my_t = (3,)
print(type(my_t), type((3)))
<class 'tuple'> <class 'int'>

Looping through a tuple · Writing over a tuple

dimensions = (200, 50)
print("Original dimensions:")
for dimension in dimensions:
    print(dimension)
dimensions = (400, 100)
print("\nModified dimensions:")
for dimension in dimensions:
    print(dimension)
Original dimensions:
200
50

Modified dimensions:
400
100

Explanation

Items of a tuple cannot be changed, but the variable can be bound to a new tuple. Python raises no error because the name is reassigned; the old tuple is not modified.

When to use a list and when to use a tuple

Security example — socket addresses are tuples

server_address = ("127.0.0.1", 8080)
host, port = server_address
print(host, port)
127.0.0.1 8080

Practice exercises (Chapter 3)

  • Names · Greetings · Your Own List — create lists, access items, use f-strings.
  • Guest List · Changing Guest List · More Guests · Shrinking Guest List — append, insert, pop, del.
  • Seeing the World · Locations · Every Function — sort(), sorted(), reverse(), len().
  • Pizzas · Animals — for loops with a final line after the loop.
  • Counting to Twenty · One Million · Summing a Million · Odd Numbers · Threes · Cubes · Cube Comprehension — range(), min/max/sum, comprehensions.
  • Slices · My Pizzas, Your Pizzas · More Loops — slicing and copying.
  • Buffet — tuples; confirm Python rejects item assignment.

Chapter 3 — key takeaways

Lists are ordered, mutable, zero-indexed; negative indexes count from the end.

Add with append()/insert(); remove with del, pop(), remove().

sort() changes the list; sorted() returns a new one; key= controls comparison.

for loops + indentation define what repeats; range() and comprehensions build numeric lists.

Slices select parts of a list and make shallow copies; assignment only creates an alias.

Tuples are immutable sequences for fixed groups of values.

Chapter 4 — If Statements

A simple example

cars = ['audi', 'bmw', 'subaru', 'toyota']
for car in cars:
    if car == 'bmw':
        print(car.upper())
    else:
        print(car.title())
Audi
BMW
Subaru
Toyota

Explanation

Inside the loop, each car is tested; 'bmw' is an acronym and is printed in uppercase, all others in title case.

Checking for equality · Checking for inequality

>>> car = 'bmw'
>>> car == 'bmw'
True
>>> car = 'audi'
>>> car == 'bmw'
False
requested_topping = 'mushrooms'
if requested_topping != 'anchovies':
    print("Hold the anchovies!")
Hold the anchovies!

Key point table

Note: every conditional test evaluates to True or False (type bool).

Ignoring case when checking for equality

>>> car = 'Audi'
>>> car == 'audi'
False
>>> car.lower() == 'audi'
True
>>> car
'Audi'

lower() returns a new string for the comparison; the stored value keeps its original case.

Security box — normalise before checking

blocked_users = ['admin', 'root']
username = ' Admin '
print(username in blocked_users)
print(username.strip().lower() in blocked_users)
False
True
  • casefold() is the recommended method for caseless matching of international text.
  • When comparing secrets (tokens, password hashes), use hmac.compare_digest() instead of == to reduce timing side channels.

Numerical comparisons

>>> age = 19
>>> age < 21
True
>>> age <= 21
True
>>> age > 21
False
>>> age >= 21
False
answer = 17
if answer != 42:
    print("That is not the correct answer. Please try again!")
That is not the correct answer. Please try again!

Extras

  • Chained comparison: 18 <= 21 < 65 → True (same as 18 <= 21 and 21 < 65).
  • Floats: 0.1 + 0.2 == 0.3 → False; use math.isclose() (link to Chapter 2).

Operator table

Checking multiple conditions — and, or

>>> age_0 = 22
>>> age_1 = 18
>>> age_0 >= 21 and age_1 >= 21
False
>>> age_1 = 22
>>> age_0 >= 21 and age_1 >= 21
True
>>> age_0 = 22
>>> age_1 = 18
>>> age_0 >= 21 or age_1 >= 21
True
>>> age_0 = 18
>>> age_0 >= 21 or age_1 >= 21
False

Truth table

  • Parentheses improve readability: (age_0 >= 21) and (age_1 >= 21).
  • Short-circuit evaluation: in A and B, B is not evaluated when A is False — useful for guards such as if users and users[0] == 'admin':

Checking whether a value is (or is not) in a list

>>> requested_toppings = ['mushrooms', 'onions', 'pineapple']
>>> 'mushrooms' in requested_toppings
True
>>> 'pepperoni' in requested_toppings
False
banned_users = ['andrew', 'carolina', 'david']
user = 'marie'
if user not in banned_users:
    print(f"{user.title()}, you can post a response if you wish.")
Marie, you can post a response if you wish.

Security box — allow-list vs block-list

  • Block-list: reject known-bad values (banned_users) — easy to bypass with unknown values.
  • Allow-list: accept only known-good values (allowed_ports = [80, 443]) — recommended by OWASP for input validation.

Performance note: x in list scans items one by one (O(n)); sets (Chapter 6) give fast lookups for large lists.

Boolean expressions and truth values (supplement)

game_active = True
can_edit = False
print(bool([]), bool([0]), bool(''), bool('0'))
False True False True

Values that are False in an if test

False, None, 0, 0.0, '' (empty string), [] (empty list), () (empty tuple), {} (empty dict), set(), range(0)

Everything else counts as True, including '0' and [0].

PEP 8 recommendations

  • For sequences, use if not seq: / if seq: rather than if len(seq) == 0:.
  • Don't compare booleans to True/False with ==.

if statements · if-else statements

age = 19
if age >= 18:
    print("You are old enough to vote!")
    print("Have you registered to vote yet?")
You are old enough to vote!
Have you registered to vote yet?
age = 17
if age >= 18:
    print("You are old enough to vote!")
else:
    print("Sorry, you are too young to vote.")
    print("Please register to vote as soon as you turn 18!")
Sorry, you are too young to vote.
Please register to vote as soon as you turn 18!

The if-elif-else chain

Version 1

age = 12
if age < 4:
    print("Your admission cost is 0 USD.")
elif age < 18:
    print("Your admission cost is 25 USD.")
else:
    print("Your admission cost is 40 USD.")
Your admission cost is 25 USD.

Version 2 (preferred — one print, easier to change)

age = 12
if age < 4:
    price = 0
elif age < 18:
    price = 25
else:
    price = 40
print(f"Your admission cost is {price} USD.")
Your admission cost is 25 USD.

Key point: Python tests conditions top-down and runs ONLY the first block whose test is True.

Using multiple elif blocks · Omitting the else block

age = 70
if age < 4:
    price = 0
elif age < 18:
    price = 25
elif age < 65:
    price = 40
elif age >= 65:
    price = 20
print(f"Your admission cost is {price} USD.")
Your admission cost is 20 USD.

Explanation

  • Any number of elif blocks is allowed.
  • Replacing the final else with an explicit elif age >= 65 makes the code reject unexpected cases: else catches EVERYTHING, including invalid or malicious input.

Optional note: Python 3.10 adds the match statement (structural pattern matching) for multi-way branching on values and shapes.

Testing multiple conditions — independent if statements

Correct — independent ifs

requested_toppings = ['mushrooms', 'extra cheese']
if 'mushrooms' in requested_toppings:
    print("Adding mushrooms.")
if 'pepperoni' in requested_toppings:
    print("Adding pepperoni.")
if 'extra cheese' in requested_toppings:
    print("Adding extra cheese.")
print("\nFinished making your pizza!")
Adding mushrooms.
Adding extra cheese.

Finished making your pizza!

Incorrect for this task — elif stops after the first match

Output would be:

Adding mushrooms.

Finished making your pizza!

Rule table

Applying if-elif-else: alert severity

failed_logins = 42
if failed_logins >= 50:
    severity = "critical"
elif failed_logins >= 20:
    severity = "high"
elif failed_logins >= 5:
    severity = "medium"
else:
    severity = "low"
print(f"{failed_logins} failed logins -> severity: {severity}")
42 failed logins -> severity: high

Notes

  • Order matters: the largest threshold must be tested first; reversing the order would label 42 as "medium".
  • Thresholds are illustrative; real values come from the organisation's monitoring policy.

Checking for special items · Checking that a list is not empty

requested_toppings = ['mushrooms', 'green peppers', 'extra cheese']
for requested_topping in requested_toppings:
    if requested_topping == 'green peppers':
        print("Sorry, we are out of green peppers right now.")
    else:
        print(f"Adding {requested_topping}.")
print("\nFinished making your pizza!")
Adding mushrooms.
Sorry, we are out of green peppers right now.
Adding extra cheese.

Finished making your pizza!
requested_toppings = []
if requested_toppings:
    for requested_topping in requested_toppings:
        print(f"Adding {requested_topping}.")
    print("\nFinished making your pizza!")
else:
    print("Are you sure you want a plain pizza?")
Are you sure you want a plain pizza?

Note: an empty list evaluates to False in an if test (see the truthiness slide).

Using multiple lists

available_toppings = ['mushrooms', 'olives', 'green peppers',
                      'pepperoni', 'pineapple', 'extra cheese']
requested_toppings = ['mushrooms', 'french fries', 'extra cheese']
for requested_topping in requested_toppings:
    if requested_topping in available_toppings:
        print(f"Adding {requested_topping}.")
    else:
        print(f"Sorry, we don't have {requested_topping}.")
print("\nFinished making your pizza!")
Adding mushrooms.
Sorry, we don't have french fries.
Adding extra cheese.

Finished making your pizza!

Security mapping (same structure)

allowed_ports = [80, 443]
requested_ports = [443, 23, 80]
for port in requested_ports:
    if port in allowed_ports:
        print(f"Port {port}: allow")
    else:
        print(f"Port {port}: deny")
Port 443: allow
Port 23: deny
Port 80: allow

Key message: available_toppings is an allow-list; the same pattern underlies firewall rules and input validation.

Practice exercises (Chapter 4)

  • Conditional Tests — at least 10 tests (5 True, 5 False) with printed predictions.
  • More Conditional Tests — string ==/!=, lower(), numeric comparisons, and/or, in, not in.
  • Alien Colors #1 — simple if (one passing version, one failing version).
  • Alien Colors #2 — if-else (5 or 10 points).
  • Alien Colors #3 — if-elif-else (green 5, yellow 10, red 15).
  • Stages of Life — baby, toddler, kid, teenager, adult, elder.
  • Favorite Fruit — five independent if statements.
  • Hello Admin — special greeting for 'admin'.
  • No Users — handle an empty list.
  • Checking Usernames — case-insensitive uniqueness check (lowercase copy of current_users).
  • Ordinal Numbers — 1st 2nd 3rd 4th ... 9th with if-elif-else inside a loop.

Session 2 summary

Chapter 3

  • Lists: indexing (0-based, negative), modifying, append/insert, del/pop/remove
  • Organizing: sort(), sorted(), key=, reverse(), len(); IndexError
  • for loops, indentation, range(), min/max/sum, comprehensions
  • Slicing, copy vs alias, tuples

Chapter 4

  • Conditional tests: ==, !=, <, <=, >, >=, and, or, in, not in, truthiness
  • if, if-else, if-elif-else; independent ifs when several actions may apply
  • if + lists: special items, empty-list checks, allow-list pattern

Next session — Chapter 5: Dictionaries

Eric Matthes, Python Crash Course, 2nd ed., No Starch Press, 2019 · Official docs: https://docs.python.org/3/