IS 4010: Application Development with Artificial Intelligence

Week 03: Python basics and control flow

Brandon M. Greenwell

Learning objectives

By the end of this notebook you will be able to:

  • Use variables, core data types, user input, and string formatting
  • Apply conditionals and loops to control program execution
  • Use the random module for games and simulations
  • Follow Python naming conventions and write clear docstrings
  • Read a failing pytest run and use GitHub Actions for continuous integration
  • Build a Mad Libs generator and a number guessing game

Prerequisites

  • The course environment: Python 3.12 through uv, set up with uv sync --locked
  • VS Code with the Python extension
  • Labs 01 and 02 complete

Resources

Every code cell below is runnable. Edit them and experiment.

Welcome to Python

A brief history of Python

  • Born on Christmas 1989: Guido van Rossum started Python as a hobby project during the Christmas holidays
  • Named after Monty Python: Not the snake! Guido was reading Monty Python’s Flying Circus scripts and thought it was a fun, short name
  • First release (1991): Python 0.9.0 featured classes, functions, and exception handling
  • Philosophy: “There should be one obvious way to do it” - emphasis on readability and simplicity
  • Current use: Widely used in education, data work, automation, web services, and tooling

Why Python matters today

  • Readability first: “Code is read more often than it’s written” - Python prioritizes human understanding
  • Versatility: Web development, data science, AI/ML, automation, scientific computing
  • Package ecosystem: PyPI distributes libraries for many application domains
  • Broad adoption: Used in web services, data analysis, automation, scientific computing, and machine learning
  • Readable starting point: Concise syntax lets us focus on program behavior
  • Transfer value: Its general-purpose syntax supports the programming foundations used throughout the course

The zen of Python

import this  # Try this in Python!

(PEP 20 - The Zen of Python)

  • “Beautiful is better than ugly”
  • “Simple is better than complex”
  • “Readability counts”
  • “There should be one obvious way to do it”
  • These principles guide everything we’ll learn

How to run Python code

There are three main ways to execute Python code, each with different use cases:

  1. Command line execution: Run complete programs from files
    • uv run python script.py - Execute a Python file in the course environment
    • uv run python -c "print('Hello World')" - Run a single command
  2. Interactive REPL: Test code snippets and explore
    • uv run python - Start the managed interactive Python shell
    • Great for testing, learning, and debugging
  3. Integrated Development Environment (IDE): Professional development

Modern Python toolchain

Python development has evolved with modern tools that make coding faster and more reliable:

  • uv: The course’s Python and project manager
    • Reproduces the committed environment from uv.lock
    • Handles virtual environments automatically
    • uv run python script.py - Run scripts in the project environment
  • ruff: Python linter and formatter
    • Reports style problems and common mistakes
    • Catches common errors and enforces style consistency
    • Integrates seamlessly with VS Code
  • Course usage: Use uv sync --locked to reproduce the environment and uv run python -m pytest to run tests

Session 1: Python fundamentals and data types


Understanding variables

  • Think of a variable as a labeled box where you can store a piece of information
  • Assignment: We use the equals sign (=) to put data into variables
  • Naming matters: Choose descriptive names that explain what the data represents
  • Python convention: Use snake_case for variable names (PEP 8 Style Guide)
  • Memory insight: Variables are actually references to objects in memory
# Good variable names (descriptive and clear)
student_name = "Grace Hopper"
birth_year = 1906
is_computer_pioneer = True
course_grade = 95.7

print(f"Student: {student_name}")
print(f"Born: {birth_year}")
print(f"Pioneer: {is_computer_pioneer}")
print(f"Grade: {course_grade}")

# Poor variable names (avoid these)
n = "Grace Hopper"    # What is 'n'?
x = 1906             # What does 'x' represent?

Python naming conventions

  • Variables and functions: snake_case (words separated by underscores)
    • user_name, calculate_total, is_valid
  • Constants: SCREAMING_SNAKE_CASE (all caps)
    • MAX_ATTEMPTS = 3, PI = 3.14159
  • Classes: PascalCase (capitalize each word)
    • StudentRecord, BankAccount, WeatherData
  • Modules: lowercase or snake_case
    • math, user_authentication
# Following Python conventions
MAX_LOGIN_ATTEMPTS = 3
user_email = "grace@navy.mil"
account_balance = 1250.75

def calculate_interest(principal, rate):
    return principal * rate

class BankAccount:
    pass

Variable assignment patterns

  • Multiple assignment: Assign several variables at once
  • Tuple unpacking: Extract values from collections
  • Chained assignment: Give the same value to multiple variables
  • Augmented assignment: Modify variables in place
# Multiple assignment
name, age, grade = "Alice", 20, "A"
print(f"{name} is {age} years old with grade {grade}")

# Tuple unpacking from a function return
def get_student_info():
    return "Bob", 21, 3.8

student_name, student_age, gpa = get_student_info()
print(f"{student_name}: age {student_age}, GPA {gpa}")

# Chained assignment
x = y = z = 0

# Augmented assignment operators
score = 85
score += 10    # Same as: score = score + 10
score *= 2     # Same as: score = score * 2

Core data types

  • str (string): Text data, enclosed in quotes (' or ")
  • int (integer): Whole numbers, positive or negative
  • float (floating-point): Numbers with decimal points
  • bool (boolean): Truth values (True or False)
  • Dynamic typing: Python figures out the type automatically
  • type() function: Use this to check what type a variable is
# Python automatically determines types
student_name = "Ada Lovelace"        # str
student_id = 12345                   # int
gpa = 3.95                          # float
is_honors_student = True            # bool
graduation_year = None              # NoneType

# Check types
print(type(student_name))    # <class 'str'>
print(type(gpa))            # <class 'float'>
print(type(is_honors_student))  # <class 'bool'>

Strings: working with text

  • Single vs double quotes: Both work the same, choose one style and be consistent
  • Triple quotes: For multi-line strings (also used for docstrings)
  • Escape sequences: Special characters like \n (newline), \t (tab)
  • String immutability: Strings cannot be changed after creation
  • Common methods: .upper(), .lower(), .strip(), .replace()
# Different ways to create strings
name = 'Alan Turing'
name = "Alan Turing"          # Equivalent
quote = """Computing machinery and intelligence
was published in 1950 by Alan Turing."""

# Escape sequences
message = "Hello\nWorld\t!"   # Hello(newline)World(tab)!
file_path = "C:\\Users\\Documents\\file.txt"  # Windows path

# String methods
name = "  alan turing  "
print(name.strip().title())   # "Alan Turing"
print(name.upper())          # "  ALAN TURING  "
print(name.replace("alan", "Alan"))  # "  Alan turing  "

Your turn: string practice

Practice working with strings:

# TODO: Create a variable with your full name (use mixed case)
my_name = ""

# TODO: Print your name in all uppercase

# TODO: Print your name in all lowercase

# TODO: Print your name in title case (First Letter Capitalized)

# TODO: Count how many letters are in your name (use len() function)

Numbers: integers and floats

  • Integers: Whole numbers, unlimited precision in Python
  • Floats: Decimal numbers, IEEE 754 standard (limited precision)
  • Scientific notation: 1.5e3 means 1.5 × 10³ = 1500
  • Arithmetic operators: +, -, *, /, // (floor division), % (modulo), ** (exponent)
  • Type conversion: int(), float(), str()
# Integer examples
students_enrolled = 150
temperature_celsius = -5
big_number = 123456789012345678901234567890  # No limits!

print(f"Students: {students_enrolled}")
print(f"Temperature: {temperature_celsius}C")

# Float examples
pi = 3.14159
temperature_fahrenheit = 23.0
scientific = 1.5e6  # 1,500,000

print(f"Pi: {pi}, scientific: {scientific}")

# Arithmetic operations
result = 17 / 3      # 5.666666666666667 (regular division)
result = 17 // 3     # 5 (floor division)
result = 17 % 3      # 2 (remainder)
result = 2 ** 10     # 1024 (exponentiation)

# Type conversions
age_str = "21"
age_int = int(age_str)    # Convert string to integer
pi_str = str(3.14159)     # Convert float to string

Your turn: number practice

# TODO: Calculate the area of a circle with radius 5
# Formula: area = π * r²
# Use 3.14159 for π
radius = 5
pi = 3.14159
# Your calculation here

# TODO: Calculate your age in days (approximate)
# Assume 365 days per year
age_in_years = 20  # Change this to your age
# Your calculation here

# TODO: Convert temperature from Celsius to Fahrenheit
# Formula: F = (C * 9/5) + 32
celsius = 25
# Your calculation here

Getting user input

  • input() function: Gets text from user via keyboard
  • Always returns strings: Even if user types numbers, you get a string
  • Type conversion needed: Use int() or float() for numeric input
  • Prompts: Include helpful messages to guide users
  • Error handling: Be prepared for invalid input (we’ll cover this later)
# Simple string input
name = input("What is your name? ")
print(f"Hello, {name}!")

# Numeric input with conversion
age_str = input("What is your age? ")
age = int(age_str)  # Convert to integer

# One-liner for numeric input
grade = float(input("Enter your grade (0-100): "))

# Multiple inputs
print("Tell me about yourself:")
name = input("Name: ")
age = int(input("Age: "))
favorite_color = input("Favorite color: ")

print(f"Hi {name}! You're {age} and love {favorite_color}.")

String formatting: making output readable

  • f-strings (modern): f"Hello {name}!" - preferred method
  • .format() method: "Hello {}!".format(name) - older but still common
  • % formatting: "Hello %s!" % name - legacy, avoid in new code
  • Formatting numbers: Control decimal places, padding, alignment
name = "Katherine Johnson"
salary = 75000.50
accuracy = 0.99999

# f-string examples (Python 3.6+)
print(f"Employee: {name}")
print(f"Salary: ${salary:,.2f}")           # $75,000.50
print(f"Accuracy: {accuracy:.2%}")         # 99.99%
print(f"Name width: {name:>20}")          # Right-aligned

# .format() method examples
print("Hello {}! You earned ${:,.2f}".format(name, salary))
print("Accuracy: {:.1%}".format(accuracy))

# Complex f-string expressions
items = ["apple", "banana", "cherry"]
print(f"We have {len(items)} fruits: {', '.join(items)}")

# Multi-line f-strings
report = f"""
Employee Report:
Name: {name}
Salary: ${salary:,.2f}
Performance: {accuracy:.1%}
"""

Your turn: string formatting practice

# TODO: Create variables for a product
product_name = "Laptop"
price = 1299.99
quantity = 5

# TODO: Print the product name and price with proper formatting
# Format: "Product: Laptop - $1,299.99"

# TODO: Calculate and print the total cost
# Format: "Total for 5 units: $6,499.95"

# TODO: If there's a 10% discount, show the new price
# Format: "After 10% discount: $1,169.99"

Python debugging tips

  • Read error messages carefully: Python gives helpful error descriptions
  • Check variable types: Use type() and print() to inspect values
  • Common mistakes: Forgetting quotes, mismatched parentheses, indentation errors
  • Use meaningful variable names: Makes debugging much easier
  • Test frequently: Don’t write 100 lines before testing
# Common debugging techniques
name = input("Enter name: ")
print(f"Debug: name = '{name}', type = {type(name)}")

# Check if conversion worked
age_str = input("Enter age: ")
try:
    age = int(age_str)
    print(f"Successfully converted '{age_str}' to {age}")
except ValueError:
    print(f"Couldn't convert '{age_str}' to a number")

Let’s build something (Part 1)

Mad Libs Generator Preview:

  • Concepts applied: Variables, input, string formatting, type conversion
  • User interaction: Collect words from user input
  • String manipulation: Use f-strings to build a story
  • Function design: Write testable, reusable code
  • Structure: Separate input/output from the function that returns the story
def generate_mad_lib(adjective, noun, verb):
    """
    Generates a short story using the provided words.
    This function is testable and reusable!
    """
    # Your task: Create a story using f-string formatting
    # Must include all three parameters in the returned string
    # Example structure: f"Once upon a time, a {adjective} {noun}..."
    pass

# How it will be used (conceptual - you'll implement the function)
# word1 = input("Give me an adjective: ")
# word2 = input("Give me a noun: ")
# word3 = input("Give me a past-tense verb: ")
# result = generate_mad_lib(word1, word2, word3)
# print(result) # Should print your creative story!

Your turn: create your own Mad Lib function

def my_mad_lib(adjective, noun, verb):
    """TODO: Replace this with a one-line description of your story."""
    # TODO: Create your own creative story using all three words
    # Make it at least 10-15 words long
    # Use f-string formatting
    pass

# Test your function
# print(my_mad_lib("sparkly", "unicorn", "danced"))

Preparing for session 2: development workflows

Suggested reading before our next class:

Why this matters: - The supplied workflow repeats the Week 03 tests after a push - The Actions log provides a second source of evidence in addition to local tests - Lab 03 asks you to use the workflow, not create or edit it


Session 2: control flow and automated testing

Conditional logic: making decisions

  • Decision making: Programs need to choose different actions based on conditions
  • Boolean expressions: Conditions that evaluate to True or False
  • Code blocks: Groups of statements that execute together
  • Indentation matters: Python uses whitespace to group code (not braces like other languages)
  • Comparison operators: ==, !=, <, >, <=, >=
# Simple condition
temperature = 75
if temperature > 70:
    print("It's warm outside!")
    print("Perfect for a walk.")

# Multiple conditions
age = 20
has_id = True

if age >= 21 and has_id:
    print("Welcome to the club!")
elif age >= 18:
    print("You can vote, but can't enter the club.")
else:
    print("Too young for either.")

Boolean operators and logic

  • Comparison operators: == (equal), != (not equal), <, >, <=, >=
  • Logical operators: and, or, not
  • Identity operators: is, is not (check if same object)
  • Membership operators: in, not in (check if item in collection)
  • Precedence: not → comparisons → andor
# Comparison examples
score = 85
grade = "B"
is_passing = score >= 60          # True
is_excellent = score >= 90        # False
is_b_grade = grade == "B"         # True

# Logical operators
has_homework = True
studied_hard = False
can_pass = has_homework and studied_hard    # False
should_study = not studied_hard             # True
might_pass = has_homework or studied_hard   # True

# Membership testing
fruits = ["apple", "banana", "cherry"]
has_apple = "apple" in fruits              # True
has_orange = "orange" not in fruits        # True

# Complex conditions
username_is_valid = True
password_is_valid = True
is_admin = False

if username_is_valid and password_is_valid and not is_admin:
    print("Regular user login successful")

Your turn: conditional practice

# TODO: Write a grade calculator
# Given a numeric score (0-100), determine the letter grade:
# A: 90-100, B: 80-89, C: 70-79, D: 60-69, F: 0-59

score = 85  # Change this to test different scores

# Your if/elif/else logic here


# TODO: Write a password strength checker
# A password is "strong" if it:
# - Is at least 8 characters long
# - Contains at least one number
# (Hint: use len() and any(char.isdigit() for char in password))

password = "secure123"  # Change this to test

# Your password strength logic here

Loops: repeating actions

  • Why loops? Avoid repetitive code, process collections, create interactive programs
  • for loops: When you know what you want to iterate over
  • while loops: When you want to repeat until a condition changes
  • Loop control: break (exit loop), continue (skip to next iteration)
  • Nested loops: Loops inside other loops
# For loop with range()
print("Countdown:")
for i in range(5, 0, -1):
    print(f"{i}...")
print("Blast off!")

# For loop with collections
students = ["Alice", "Bob", "Charlie"]
for student in students:
    print(f"Hello, {student}!")

# While loop for user input
total = 0
while total < 100:
    number = int(input("Enter a number (goal: reach 100): "))
    total += number
    print(f"Current total: {total}")
print("Goal reached!")

# Loop with break and continue
for i in range(10):
    if i == 3:
        continue  # Skip 3
    if i == 7:
        break     # Stop at 7
    print(i)      # Prints: 0, 1, 2, 4, 5, 6

The range() function: a loop companion

  • range(stop): Numbers from 0 to stop-1
  • range(start, stop): Numbers from start to stop-1
  • range(start, stop, step): Numbers from start to stop-1, incrementing by step
  • Memory efficient: Generates numbers on-demand, not all at once
  • Convert to list: list(range(5)) to see all values
# Basic range patterns
print(list(range(5)))           # [0, 1, 2, 3, 4]
print(list(range(1, 6)))        # [1, 2, 3, 4, 5]
print(list(range(0, 10, 2)))    # [0, 2, 4, 6, 8]
print(list(range(10, 0, -1)))   # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

# Practical examples
# Print multiplication table
number = 7
for i in range(1, 11):
    print(f"{number} x {i} = {number * i}")

# Process items with indices
fruits = ["apple", "banana", "cherry"]
for i in range(len(fruits)):
    print(f"Item {i}: {fruits[i]}")

# Better way: enumerate()
for i, fruit in enumerate(fruits):
    print(f"Item {i}: {fruit}")

Your turn: loop practice

# TODO: Print all even numbers from 0 to 20


# TODO: Calculate the sum of numbers 1 to 100
# (Hint: use a for loop and accumulate the total)


# TODO: Print the first 10 squares (1², 2², 3², etc.)


# TODO: Create a while loop that counts down from 10 to 1
# Print "Happy New Year!" at the end

Let’s build something (Part 2)

Number Guessing Game Preview:

  • Concepts applied: while loops, conditionals, user input, random numbers
  • Game logic: Generate secret number, get user guesses, provide feedback
  • Loop design: Continue until correct guess is made
  • User experience: Clear prompts and helpful feedback
  • Random module: Introduction to Python’s standard library
import random

def guessing_game():
    """Play the guessing game described in the Lab 03 contract."""
    secret = random.randint(1, 100)
    attempts = 0

    while True:
        guess = int(input("Enter your guess: "))
        # TODO: count the attempt
        # TODO: print "Too low!", "Too high!", or the success message
        # TODO: exit the loop after the correct guess

Your turn: enhance the guessing game

Modify the game to add features:

import random

def enhanced_guessing_game():
    """
    Enhanced version of the guessing game with additional features.
    """
    secret = random.randint(1, 100)
    attempts = 0
    max_attempts = 10  # Add a maximum number of attempts
    
    print("Enhanced Number Guessing Game!")
    print(f"I'm thinking of a number between 1 and 100.")
    print(f"You have {max_attempts} attempts to guess it.")
    
    while attempts < max_attempts:
        # TODO: Add the game logic here
        # 1. Get user input
        # 2. Increment attempts
        # 3. Check if guess is correct, too high, or too low
        # 4. If correct, congratulate and break
        # 5. If wrong, give feedback and continue
        # 6. If max attempts reached, reveal the number
        pass

# Test your enhanced game
# enhanced_guessing_game()

Python ecosystem preview

  • Standard library: Batteries included - modules like random, math, datetime
  • Third-party packages: Over 400,000 packages on PyPI
  • Project manager: uv manages Python, dependencies, environments, and lockfiles
  • Virtual environments: uv sync creates an isolated project environment
  • Popular packages: requests (web), pandas (data), flask (web apps)
# Standard library examples
import random
import math
import datetime

# Random numbers for games, simulations
lucky_number = random.randint(1, 10)
coin_flip = random.choice(["heads", "tails"])

# Mathematical functions
area = math.pi * (radius ** 2)
square_root = math.sqrt(16)

# Date and time operations
today = datetime.date.today()
formatted = today.strftime("%B %d, %Y")

# Coming later: third-party packages
# In the terminal for your own project: uv add requests
# import requests
# response = requests.get("https://api.github.com/users/bgreenwell")

Development practices

  • Testing mindset: Write code that proves your functions work correctly
  • Continuous Integration (CI): Automatically test code every time changes are made
  • Feedback: Catch mismatches between the implementation and the tested contract
  • Documentation: Document behavior that a reader cannot infer from the name and signature
  • Version control integration: Tests run automatically when you push to GitHub

Testing with pytest

  • Course tool: We use pytest for readable tests and automatic discovery
  • Simple conventions: Files start with test_, functions start with test_
  • Automatic discovery: pytest finds and runs all your tests
  • Clear feedback: Detailed output when tests pass or fail
  • Easy assertions: Use simple assert statements to check results
# In your lab03.py file
def add_numbers(a, b):
    """Add two numbers and return the result."""
    return a + b

# In test_lab03.py file
def test_add_numbers():
    """Test that add_numbers works correctly."""
    result = add_numbers(2, 3)
    assert result == 5
    
    result = add_numbers(-1, 1)
    assert result == 0
    
    result = add_numbers(0, 0)
    assert result == 0

Your turn: write a test

Write a test for a grade calculator function:

def calculate_letter_grade(score):
    """Convert numeric score to letter grade."""
    if score >= 90:
        return 'A'
    elif score >= 80:
        return 'B'
    elif score >= 70:
        return 'C'
    elif score >= 60:
        return 'D'
    else:
        return 'F'

# TODO: Write a test function for calculate_letter_grade
def test_calculate_letter_grade():
    """Test the grade calculator."""
    # TODO: Test that 95 returns 'A'
    
    # TODO: Test that 85 returns 'B'
    
    # TODO: Test that 75 returns 'C'
    
    # TODO: Test that 65 returns 'D'
    
    # TODO: Test that 55 returns 'F'
    
    print("All grade calculator tests passed!")

# Run your test
# test_calculate_letter_grade()

GitHub Actions: automation in the cloud

  • Repository integration: The course repository already contains the workflow file
  • Event-driven: Runs automatically when you push code
  • Hosted runner: Starts a fresh Linux runner for the job
  • Visible evidence: Records each step, command, and result in the Actions log
# The supplied Week 03 workflow follows this pattern
name: run-pytest

on: [push]  # Run when code is pushed

jobs:
  build:
    runs-on: ubuntu-latest  # Fresh Linux machine
    steps:
      - uses: actions/checkout@v6      # Get your code
      - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9
        with:
          enable-cache: true
      - name: Install dependencies
        run: uv sync --locked
      - name: Test with pytest
        run: uv run python -m pytest  # Run all tests

Your feedback loop

  • Green checkmark ✅: All tests passed - your code is working!
  • Red X ❌: Some tests failed - time to debug and fix
  • Actions tab: Click to see detailed test results and error messages
  • Iterative process: Fix issues, commit, push, and test again
  • Review habit: Investigate failed tests before merging or submitting
# From the repository root
uv run --directory week03 python -m pytest tests/ -v
git add week03/lab03.py
git commit -m "Implement mad libs and guessing game"
git push

# Then check GitHub Actions tab for results
# Green = success, Red = needs fixing

Introducing Lab 03: Python basics and automated testing

What you’ll build:

  • Part 1: Implement generate_mad_lib(adjective, noun, verb)
  • Part 2: Implement an interactive guessing_game()
  • Provided tests: Run the Week 03 tests without modifying them
  • Submission: Commit week03/lab03.py, push, and confirm the Week 03 badge is green

Key learning outcomes: - Practice strings, conditionals, loops, input, and functions - Use deterministic tests for random numbers and keyboard input - Read failures locally before pushing


Time for Lab 03

Use the current lab instructions as the source of truth:

  • Navigate to: week03/lab03.md
  • Build: Mad Libs generator and number guessing game
  • Test: uv run --directory week03 python -m pytest tests/ -v
  • Submit: week03/lab03.py

Remember: A green Week 03 badge represents the complete 10-point lab.