1

print() with keyword arguments

Learning objective: After this step you will be able to use print()’s sep and end keyword arguments and unpack a list into separate arguments with *.

The print() signature

print(*args, sep=' ', end='\n', file=None, flush=False)
Argument Default Effect
sep ' ' String inserted between multiple positional arguments
end '\n' String appended after the last argument

sep acts between arguments — use * to unpack a list

When you pass multiple separate values, sep appears between them:

print("Hello", "World", sep='_')    # Hello_World

To use sep with a list, prefix it with * to unpack it into separate arguments:

print(*["Hello", "World"], sep='_')  # Hello_World

Without * the list is treated as a single argument, so sep has nothing to separate and Python prints the list’s string representation instead:

print(["Hello", "World"], sep='_')  # ['Hello', 'World']

Task

Write one print() call that unpacks ["Hello", "World", "Students"] with *, uses sep='_' and end='!\n'.

Expected output:

Hello_World_Students!
Starter files
hello.py
# What do you expect from this point? Predict the outcome then run the command
print(["Hello", "World"], sep='_')  

# Write one print() call that unpacks the list with *
# and uses sep='_' and end='!\n'
# Expected output: Hello_World_Students!
2

Sequences, List Comprehension & Unpacking

Learning objective: After this step you will be able to write list comprehensions using range(), apply min(), max(), and sum() to lists, and unpack and swap variables with a single assignment.

List comprehensions

A list comprehension creates a new list by applying an expression to every element of a sequence:

[expr(x) for x in sequence]
Example Result
[x for x in range(1, 10)] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[2*x for x in range(4)] [0, 2, 4, 6]

range(start, end) — integer sequences

range(start, end) generates integers from start up to end - 1 (the upper bound is exclusive). The default for start is 0, so range(4) is the same as range(0, 4).

Built-in aggregation: min, max, sum

These are built-in functions — no import needed:

nums = [3, 1, 4, 1, 5]
print(min(nums))   # 1
print(max(nums))   # 5
print(sum(nums))   # 14

Unpacking and swapping

Python lets you unpack a list directly into named variables:

[a, b] = [1, 2]
print(a)   # 1
print(b)   # 2

And you can swap two variables in one line — no temporary variable needed:

a, b = b, a
print(a)   # 2
print(b)   # 1

Python evaluates the entire right-hand side first, then performs all assignments simultaneously.

“Syntactic sugar” refers to language features that make code easier to write without adding new capabilities.

Task

Complete the four TODOs in the starter code:

  1. Replace evens = [] with a list comprehension using 2*x that produces [0, 2, 4, 6, 8].
  2. Print min, max, and sum of evens.
  3. Unpack the first two elements of evens into a and b.
  4. Swap a and b in one line.

Expected output:

[0, 2, 4, 6, 8]
0
8
20
Before swap: a=0, b=2
After swap:  a=2, b=0
Starter files
sequences.py
# 1. Create a list of even numbers 0, 2, 4, 6, 8 using a list comprehension
evens = []  # TODO: replace with a list comprehension

print(evens)

# 2. TODO: print min(evens), max(evens), and sum(evens) — one call per line

# 3. Unpack the first two elements of evens into a and b
a = 0  # TODO: replace these two lines with  [a, b] = [evens[0], evens[1]]
b = 0
print(f"Before swap: a={a}, b={b}")

# 4. Swap a and b in one line, then print again
# TODO: replace this comment with  a, b = b, a
print(f"After swap:  a={a}, b={b}")
3

Classes and __str__

Learning objective: After this step you will be able to define a Python class with __init__ and __str__, and explain how print(), str(), and f-strings all invoke __str__ automatically.

Python classes

class Point:
    def __init__(self, x, y):  # constructor
        self.x = x    # instance attribute
        self.y = y

    def __str__(self):        # string representation
        return f'Point({self.x}, {self.y})'

p = Point(3, 4)
print(p)            # Point(3, 4)
print(str(p))       # Point(3, 4)
print(f"At: {p}")   # At: Point(3, 4)
Dunder method Called automatically by
__init__ Point(3, 4) — object construction
__str__ print(obj), str(obj), and f-strings f"{obj}"

All instance methods receive self as the first parameter — Python passes the object automatically when you call a method on an instance.

Task

The starter code gives you the Book class with __init__ already complete.

  1. Add a __str__ method inside the class that returns the book as a formatted string:
    "<title>" by <author> (<year>)
    

    Use an f-string. Note the literal double-quotes around the title.

  2. Create an instance below the class:
    my_book = Book("Pride and Prejudice", "Jane Austen", 1813)
    
  3. Print it three ways and observe that all three invoke __str__ automatically:
    print(my_book)                        # 1. via print()
    print(str(my_book))                   # 2. via str()
    print(f"The book is: {my_book}")      # 3. via f-string
    

Expected output:

"Pride and Prejudice" by Jane Austen (1813)
"Pride and Prejudice" by Jane Austen (1813)
The book is: "Pride and Prejudice" by Jane Austen (1813)
Starter files
book.py
class Book:
    """Represents a book with a title and an author."""

    def __init__(self, title, author, year):
        self.title = title
        self.author = author
        self.year = year

    # Add your __str__ method here

# Create an instance of the class
my_book = Book("Pride and Prejudice", "Jane Austen", 1813)

# Print my_book three ways below
4

Named Groups: Regex on a Requirements Document

Learning objective: After this step you will be able to apply named capture groups, re.compile(), and re.finditer() to extract structured data from an HTML requirements document.

Named capture groups

So far you have used patterns that match a region of text and return the entire match. Named groups let you label sub-parts of a match and retrieve them by name:

import re

text = "Error 404: page not found"

m = re.search(r'Error (?P<code>\d+)', text)
if m:
    print(m.group('code'))   # '404'

The syntax (?P<name>...) is identical to a regular group (...) but each captured sub-string can be retrieved by name with m.group('name').

re.compile() + re.finditer()

re.compile(pattern, flags) pre-compiles a pattern into a reusable object. Its .finditer() method returns an iterator of match objects so you can access named groups from each result:

text = "apple costs $1.20, banana costs $0.45"

pattern = re.compile(r'(?P<fruit>\w+) costs \$(?P<price>[\d.]+)')
for m in pattern.finditer(text):
    print(m.group('fruit'), '', m.group('price'))
# apple → 1.20
# banana → 0.45

New flag: re.MULTILINE

By default, ^ matches only the very start of the whole string. Pass re.MULTILINE to re.compile() to make ^ match the start of every line:

text = "line one\nAs a user, I want X so that Y\nline three"

# Without re.MULTILINE — ^ only anchors to the start of the string — no match
pattern = re.compile(r'^As an? (?P<user>.+), I want to .+ so that .+')
print(list(pattern.finditer(text)))   # []

# With re.MULTILINE — ^ anchors to the start of each line
pattern = re.compile(r'^As an? (?P<user>.+), I want to .+ so that .+', re.MULTILINE)
for m in pattern.finditer(text):
    print(m.group('user'))   # user

Matching across lines with \n

A plain . does not match a newline. To span multiple lines, include \n explicitly:

text = "Given X is true\n    When Y happens\n    Then Z occurs"

pattern = re.compile(r'Given (?P<given>.+)\n.* When (?P<when>.+)\n*.* Then (?P<then>.+)', re.MULTILINE)
for m in pattern.finditer(text):
    print(m.group('given'), '/', m.group('when'), '/', m.group('then'))

Task

The starter file reads requirements.html into content and provides a match_regex helper that applies re.MULTILINE automatically. Define the four patterns and call match_regex to find:

  1. User stories — lines starting with As a/an …, I want to … so that … Print: User stories: <count> then each user group on its own indented line

  2. Headings — all <h1>…</h1> tags Print: Headings: <count> then each heading text on its own indented line

  3. Acceptance criteria — three-line Given/When/Then blocks Print: Acceptance criteria: <count> then for each: Given:, When:, Then: lines

  4. File references — things like .pdf file, .zip file Print: File references: <count> then each extension on its own indented line

Starter files
html_parser.py
import re

def match_regex(pattern):
    # re.MULTILINE makes ^ match the start of each line
    compiled_pattern = re.compile(pattern, re.MULTILINE)
    return list(compiled_pattern.finditer(content))

with open('requirements.html', 'r', encoding='utf-8') as f:
    content = f.read()

# 1. User stories
# e.g., As a reviewer, I want to be able to download all submitted files so that I can review them efficiently offline.

# 2. Headings
# e.g. <h1>Heading Text</h1>'

# 3. Acceptance criteria
# e.g., 'Given the user is logged in and submitted papers\n When they go to my paper\n Then they see their papers.'

# 4. File references
# e.g: .pdf file
requirements.html
<h1>Bulk Download for Reviewers</h1>

As a reviewer (or administrator), I want to be able to download all submitted files for a specific application in a single compressed folder (ZIP) so that I can review the materials efficiently offline.

    Given I am logged in as a registered reviewer any many submissions have been created already,
    When I navigate to the submission page of a valid submission
    Then I see a "Download All Submission Files" button,

    Given I am logged in as a registered reviewer and viewing the details of a specific, valid submission,
    When I click the "Download All Submission Files" button,
    Then The system should generate and prompt me to download a single .zip file containing every file uploaded by the applicant for that submission.

    Given I am logged in as an applicant who created a valid submission before,
    When I navigate to the submission page of my own own submission,
    Then I do not see a "Download All Submission Files" button,

<h1>Applicant Registration</h1>

As an applicant, I want to be able to register and create a profile so that I can easily track my submissions and receive official communications.

    Given I am a first-time user and I am on the system's registration page,
    When I fill in all required fields and click the "Register" button,
    Then My account should be created, and I should receive an email confirmation with a link to my new profile dashboard.

<h1>Multi-File Upload</h1>

As an applicant, I want to be able to upload multiple files (e.g., a PDF of my paper and a separate image file) so that I can provide all the required materials for my submission in one place.

    Given I am creating a new submission with a pdf and a image, and both files are under the maximum size limit,
    When I select both a .pdf file and a .jpg file using the upload tool,
    Then Both files should be successfully attached to my submission and displayed in the confirmation summary.

<h1>Submission Confirmation</h1>

As an applicant, I want to receive an immediate email confirmation after successfully submitting my materials so that I have proof that my submission was received by the system.

    Given I have completed all required fields and files for my application,
    When I click the final "Submit Application" button,
    Then The system should display a success message and immediately send an automated email containing a unique Submission ID and a summary of my provided data.

<h1>Administrative Filtering and Search</h1>

As an administrator, I want to be able to search and filter submissions based on criteria like submission status (e.g., "Pending," "Accepted," "Rejected") and applicant name so that I can quickly manage and process the application pool.

    Given I am logged in as an administrator and viewing the main submissions list and there are many submissions in the system, some of which were submitted by user "Smith" and some of these have been accepted,
    When I apply the filter for "Status: Accepted" and search for "Smith"
    Then The list should only display submissions that have an Accepted status and contain "Smith" in the applicant's name field.