You are viewing a preview of this lesson. Sign in to start learning
Back to Python Programming

Control Flow & Functions

Learn conditional statements, loops, and function definitions for program logic

Control Flow & Functions in Python

Master Python's control flow and function fundamentals with free flashcards and spaced repetition practice. This lesson covers conditional statements, loops, function definitions, parameters, return values, and scopeβ€”essential concepts for writing efficient, reusable Python code.

Welcome to Control Flow & Functions! πŸ’»

Every program needs to make decisions and repeat tasks. Without control flow, your code would just execute line by line with no flexibility. Without functions, you'd repeat the same code over and over. Think of control flow as the decision-making brain of your program, and functions as reusable building blocks that keep your code organized and maintainable.

In this lesson, you'll learn how to:

  • Control program execution with if/elif/else statements
  • Repeat tasks efficiently using for and while loops
  • Create reusable code with function definitions
  • Pass data using parameters and arguments
  • Return results and understand variable scope

Core Concepts: Control Flow πŸ”€

Conditional Statements: Making Decisions

Conditional statements allow your program to choose different paths based on conditions. The basic structure uses if, elif (else if), and else.

Syntax:

if condition:
    # code block executes if condition is True
elif another_condition:
    # code block executes if first is False and this is True
else:
    # code block executes if all above are False

Key points:

  • Conditions evaluate to True or False (Boolean values)
  • Indentation (4 spaces) defines code blocks
  • elif and else are optional
  • Only ONE block executes (the first True condition)

πŸ’‘ Tip: Python uses indentation instead of braces {} like other languages. Consistent indentation is critical!

Comparison Operators

OperatorMeaningExampleResult
==Equal to5 == 5True
!=Not equal5 != 3True
<Less than3 < 5True
>Greater than5 > 3True
<=Less or equal5 <= 5True
>=Greater or equal6 >= 5True

Logical Operators

Combine multiple conditions using logical operators:

  • and - Both conditions must be True
  • or - At least one condition must be True
  • not - Inverts the Boolean value
age = 25
has_license = True

if age >= 18 and has_license:
    print("Can drive")

🧠 Memory device: Think "AND = All must be true, OR = Only one needed"


Core Concepts: Loops πŸ”

For Loops: Iterate Over Sequences

A for loop repeats code for each item in a sequence (list, string, range, etc.).

Syntax:

for variable in sequence:
    # code block executes for each item

The range() function:

  • range(n) - generates 0 to n-1
  • range(start, stop) - generates start to stop-1
  • range(start, stop, step) - generates with custom step
for i in range(5):  # 0, 1, 2, 3, 4
    print(i)

for i in range(2, 8, 2):  # 2, 4, 6
    print(i)

πŸ’‘ Real-world analogy: A for loop is like a delivery driver with a list of addressesβ€”they visit each one systematically until the list is complete.

While Loops: Repeat While Condition is True

A while loop continues executing as long as its condition remains True.

Syntax:

while condition:
    # code block executes while condition is True
count = 0
while count < 5:
    print(count)
    count += 1  # IMPORTANT: update the condition variable!

⚠️ Warning: Always ensure your while loop will eventually become False, or you'll create an infinite loop!

Loop Control: break and continue

  • break - Exit the loop immediately
  • continue - Skip to the next iteration
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

Core Concepts: Functions πŸ“¦

Function Definitions

A function is a reusable block of code that performs a specific task. Functions help you:

  • Organize code into logical units
  • Avoid repetition (DRY: Don't Repeat Yourself)
  • Make code easier to test and debug
  • Enable code reuse across projects

Syntax:

def function_name(parameters):
    """Optional docstring describing the function"""
    # code block
    return value  # optional

Anatomy of a function:

  • def - keyword to define a function
  • function_name - follows same rules as variable names
  • parameters - inputs the function accepts (optional)
  • return - sends a value back to the caller (optional)
def greet(name):
    """Greets a person by name"""
    return f"Hello, {name}!"

message = greet("Alice")  # Call the function
print(message)  # Hello, Alice!

Parameters vs Arguments

These terms are often confused:

TermDefinitionExample
ParameterVariable in function definitiondef func(x, y): ← x, y are parameters
ArgumentActual value passed when callingfunc(5, 10) ← 5, 10 are arguments

Default Parameters

Parameters can have default values used when no argument is provided:

def power(base, exponent=2):
    return base ** exponent

print(power(3))      # 9 (uses default exponent=2)
print(power(3, 3))   # 27 (overrides default)

πŸ’‘ Tip: Default parameters must come AFTER non-default parameters!

Return Values

The return statement:

  • Sends a value back to the caller
  • Immediately exits the function
  • Can return multiple values as a tuple
  • If omitted, function returns None
def calculate(a, b):
    sum_val = a + b
    product = a * b
    return sum_val, product  # returns tuple

result1, result2 = calculate(5, 3)  # unpacking
print(result1, result2)  # 8 15

πŸ”§ Try this: Write a function that returns nothing but prints. Notice it returns None:

def say_hello():
    print("Hello!")

result = say_hello()  # prints "Hello!"
print(result)  # None

Variable Scope πŸ”­

Scope determines where a variable can be accessed:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      GLOBAL SCOPE                   β”‚
β”‚  (accessible everywhere)            β”‚
β”‚                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚   FUNCTION SCOPE              β”‚ β”‚
β”‚  β”‚   (local to this function)    β”‚ β”‚
β”‚  β”‚                               β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚  β”‚  β”‚  NESTED FUNCTION SCOPE  β”‚ β”‚ β”‚
β”‚  β”‚  β”‚  (innermost scope)      β”‚ β”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

LEGB Rule (scope resolution order):

  1. Local - inside current function
  2. Enclosing - in parent function(s)
  3. Global - at module level
  4. Built-in - Python's built-in names
x = 10  # global variable

def outer():
    x = 20  # enclosing variable
    
    def inner():
        x = 30  # local variable
        print(x)  # 30 (local)
    
    inner()
    print(x)  # 20 (enclosing)

outer()
print(x)  # 10 (global)

πŸ’‘ Best practice: Avoid using global variables inside functions. Pass values as parameters instead!

The global keyword: To modify a global variable inside a function:

counter = 0

def increment():
    global counter  # declare we're using global variable
    counter += 1

increment()
print(counter)  # 1

Detailed Examples πŸ“

Example 1: Grade Calculator with Conditionals

Let's build a function that converts numeric scores to letter grades:

def get_letter_grade(score):
    """
    Converts numeric score (0-100) to letter grade.
    
    Args:
        score: int or float between 0 and 100
    
    Returns:
        str: Letter grade (A, B, C, D, or F)
    """
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

# Test the function
print(get_letter_grade(95))  # A
print(get_letter_grade(82))  # B
print(get_letter_grade(55))  # F

How it works:

  1. Function accepts a score parameter
  2. Checks conditions from highest to lowest
  3. Returns immediately when first condition is True
  4. The else catches all remaining cases (score < 60)

πŸ€” Did you know? The order of conditions matters! If we checked score >= 60 first, a score of 95 would return "D" because 95 is indeed >= 60.

Enhanced version with input validation:

def get_letter_grade(score):
    """Converts score to letter grade with validation"""
    if score < 0 or score > 100:
        return "Invalid score"
    elif score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

print(get_letter_grade(105))  # Invalid score
print(get_letter_grade(-5))   # Invalid score

Example 2: Factorial Calculator with While Loop

Calculate factorial (n! = n Γ— (n-1) Γ— ... Γ— 2 Γ— 1):

def factorial(n):
    """
    Calculates factorial using while loop.
    
    Args:
        n: non-negative integer
    
    Returns:
        int: factorial of n
    """
    if n < 0:
        return None  # undefined for negative numbers
    
    result = 1
    current = n
    
    while current > 1:
        result *= current
        current -= 1
    
    return result

# Test
print(factorial(5))   # 120 (5 Γ— 4 Γ— 3 Γ— 2 Γ— 1)
print(factorial(0))   # 1 (by definition)
print(factorial(-3))  # None

Step-by-step execution for factorial(5):

IterationcurrentresultOperation
Start51Initialize
1551 Γ— 5 = 5
24205 Γ— 4 = 20
336020 Γ— 3 = 60
4212060 Γ— 2 = 120
End1120Loop exits (1 not > 1)

Alternative with for loop:

def factorial(n):
    """Calculates factorial using for loop"""
    if n < 0:
        return None
    
    result = 1
    for i in range(2, n + 1):
        result *= i
    
    return result

πŸ’‘ Which is better? The for loop version is more Pythonic and concise. Use for loops when you know how many iterations you need, while loops when you don't.


Example 3: List Processing with Multiple Functions

Let's build a system to analyze a list of numbers:

def calculate_average(numbers):
    """
    Calculates the average of a list of numbers.
    
    Args:
        numbers: list of int or float
    
    Returns:
        float: average value, or 0 if list is empty
    """
    if len(numbers) == 0:
        return 0
    
    total = 0
    for num in numbers:
        total += num
    
    return total / len(numbers)


def find_maximum(numbers):
    """
    Finds the largest number in a list.
    
    Args:
        numbers: list of int or float
    
    Returns:
        int/float: maximum value, or None if empty
    """
    if len(numbers) == 0:
        return None
    
    max_value = numbers[0]  # assume first is max
    
    for num in numbers:
        if num > max_value:
            max_value = num
    
    return max_value


def count_above_threshold(numbers, threshold):
    """
    Counts how many numbers exceed a threshold.
    
    Args:
        numbers: list of int or float
        threshold: the cutoff value
    
    Returns:
        int: count of numbers above threshold
    """
    count = 0
    
    for num in numbers:
        if num > threshold:
            count += 1
    
    return count


# Using the functions together
scores = [85, 92, 78, 90, 88, 76, 95]

avg = calculate_average(scores)
max_score = find_maximum(scores)
count_a = count_above_threshold(scores, 90)

print(f"Average: {avg:.2f}")          # Average: 86.29
print(f"Highest: {max_score}")        # Highest: 95
print(f"A grades (>90): {count_a}")   # A grades (>90): 2

Design principles demonstrated:

  • Single Responsibility: Each function does ONE thing well
  • Reusability: Functions can be used independently or together
  • Defensive Programming: Check for empty lists
  • Clear Documentation: Docstrings explain purpose, args, returns

🌍 Real-world connection: This pattern is used in data analysis, financial reporting, student grade systems, and analytics dashboards everywhere!


Example 4: Nested Loops for Pattern Generation

Create a multiplication table using nested loops:

def print_multiplication_table(size):
    """
    Prints a multiplication table up to size Γ— size.
    
    Args:
        size: int, the dimensions of the table
    """
    # Print header row
    print("    ", end="")
    for i in range(1, size + 1):
        print(f"{i:4}", end="")
    print()  # newline
    print("    " + "─" * (size * 4))  # separator line
    
    # Print each row
    for row in range(1, size + 1):
        print(f"{row:2} β”‚", end="")
        for col in range(1, size + 1):
            product = row * col
            print(f"{product:4}", end="")
        print()  # newline after each row

# Generate 5Γ—5 table
print_multiplication_table(5)

Output:

       1   2   3   4   5
    ────────────────────
 1 β”‚   1   2   3   4   5
 2 β”‚   2   4   6   8  10
 3 β”‚   3   6   9  12  15
 4 β”‚   4   8  12  16  20
 5 β”‚   5  10  15  20  25

How nested loops work:

Outer loop (row=1):
    Inner loop: col=1,2,3,4,5 β†’ prints 1,2,3,4,5
Outer loop (row=2):
    Inner loop: col=1,2,3,4,5 β†’ prints 2,4,6,8,10
Outer loop (row=3):
    Inner loop: col=1,2,3,4,5 β†’ prints 3,6,9,12,15
...

πŸ’‘ Key insight: The inner loop completes ALL iterations for EACH iteration of the outer loop.

Pattern variations:

def print_triangle(height):
    """Prints a right triangle of asterisks"""
    for row in range(1, height + 1):
        for col in range(row):
            print("*", end="")
        print()  # newline

print_triangle(5)
# Output:
# *
# **
# ***
# ****
# *****

Common Mistakes ⚠️

1. Indentation Errors

❌ Wrong:

def greet(name):
print(f"Hello, {name}")  # IndentationError!

βœ… Right:

def greet(name):
    print(f"Hello, {name}")  # properly indented

Fix: Always indent code blocks with 4 spaces (or 1 tab, but be consistent).


2. Using Assignment (=) Instead of Comparison (==)

❌ Wrong:

if x = 5:  # SyntaxError: assigns instead of compares
    print("x is 5")

βœ… Right:

if x == 5:  # compares x with 5
    print("x is 5")

Fix: Remember: = assigns, == compares!


3. Forgetting the Colon (:)

❌ Wrong:

if x > 10  # SyntaxError: missing colon
    print("Large")

def calculate(x)  # SyntaxError: missing colon
    return x * 2

βœ… Right:

if x > 10:  # colon required
    print("Large")

def calculate(x):  # colon required
    return x * 2

Fix: All control flow statements and function definitions end with :


4. Infinite Loops

❌ Wrong:

count = 0
while count < 5:
    print(count)
    # Forgot to increment count - runs forever!

βœ… Right:

count = 0
while count < 5:
    print(count)
    count += 1  # update condition variable

Fix: Always ensure your loop condition will eventually become False!


5. Modifying Global Variables Without 'global' Keyword

❌ Wrong:

counter = 0

def increment():
    counter += 1  # UnboundLocalError!

increment()

βœ… Right:

counter = 0

def increment():
    global counter  # declare global usage
    counter += 1

increment()

Better: Pass as parameter and return:

def increment(counter):
    return counter + 1

counter = 0
counter = increment(counter)

6. Returning Inside a Loop Prematurely

❌ Wrong:

def find_even(numbers):
    for num in numbers:
        if num % 2 == 0:
            return num  # returns first even, not all
    return None

result = find_even([1, 3, 4, 6, 8])
print(result)  # 4 only, not [4, 6, 8]

βœ… Right (if you want all evens):

def find_all_evens(numbers):
    evens = []
    for num in numbers:
        if num % 2 == 0:
            evens.append(num)
    return evens

result = find_all_evens([1, 3, 4, 6, 8])
print(result)  # [4, 6, 8]

7. Off-by-One Errors with range()

❌ Wrong:

for i in range(10):  # 0 to 9, NOT 1 to 10
    print(i)  # prints 0,1,2,3,4,5,6,7,8,9

βœ… Right (if you want 1-10):

for i in range(1, 11):  # 1 to 10
    print(i)  # prints 1,2,3,4,5,6,7,8,9,10

Remember: range(n) generates 0 to n-1, range(start, stop) generates start to stop-1


8. Not Returning a Value

❌ Wrong:

def add(a, b):
    result = a + b
    # forgot return statement

total = add(5, 3)
print(total)  # None

βœ… Right:

def add(a, b):
    result = a + b
    return result  # or: return a + b

total = add(5, 3)
print(total)  # 8

Key Takeaways 🎯

βœ… Control Flow:

  • Use if/elif/else for decisions based on conditions
  • Conditions evaluate to Boolean values (True/False)
  • Indentation defines code blocks
  • Logical operators (and, or, not) combine conditions

βœ… Loops:

  • for loops iterate over sequences (lists, strings, ranges)
  • while loops continue until condition becomes False
  • break exits loop immediately, continue skips to next iteration
  • Always ensure while loops will eventually terminate

βœ… Functions:

  • Define with def function_name(parameters):
  • Parameters are inputs, arguments are actual values passed
  • Use return to send values back to caller
  • Functions without return implicitly return None
  • Write docstrings to document purpose, parameters, and return values

βœ… Scope:

  • Variables have limited scope (local, enclosing, global, built-in)
  • Local variables exist only within their function
  • Use global keyword to modify global variables (but avoid when possible)
  • Pass values as parameters instead of using globals

βœ… Best Practices:

  • Keep functions small and focused (single responsibility)
  • Use descriptive function and variable names
  • Add input validation to functions
  • Write docstrings for documentation
  • Prefer for loops when iteration count is known
  • Test functions with various inputs including edge cases

πŸ“š Further Study

  1. Python Official Tutorial - Control Flow: https://docs.python.org/3/tutorial/controlflow.html
  2. Real Python - Python Functions: https://realpython.com/defining-your-own-python-function/
  3. Python Official Documentation - Built-in Functions: https://docs.python.org/3/library/functions.html

πŸ“‹ Quick Reference Card

ConceptSyntaxExample
If Statementif condition:if x > 5: print("big")
If-Elif-Elseif...elif...else:if x > 10: ... elif x > 5: ... else: ...
For Loopfor var in seq:for i in range(5): print(i)
While Loopwhile condition:while x < 10: x += 1
BreakbreakExit loop immediately
ContinuecontinueSkip to next iteration
Functiondef name(params):def add(x, y): return x + y
Returnreturn valuereturn x * 2
Default Paramdef f(x=0):def greet(name="World"): ...
Global Variableglobal varglobal counter

Comparison Operators: == equal, != not equal, < less, > greater, <= less/equal, >= greater/equal

Logical Operators: and (both true), or (at least one true), not (invert)

Range Function: range(n) β†’ 0 to n-1, range(start, stop) β†’ start to stop-1, range(start, stop, step)

LEGB Scope Order: Local β†’ Enclosing β†’ Global β†’ Built-in


Practice makes perfect! πŸš€ Experiment with these concepts in your Python environment. Try modifying the examples, create your own functions, and challenge yourself with increasingly complex control flow logic. The more you code, the more natural these patterns will become!

Practice Questions

Test your understanding with these questions:

Q1: Write a Python function named 'is_even' that takes an integer parameter 'n' and returns True if the number is even, False otherwise. Use the modulo operator (%).
A: !AI