All Posts
pythonPart 3 of python-basics-to-advanced

Python #3 — Control Flow & Functions

if/elif/else, for and while loops, break/continue, and writing clean Python functions with default arguments, *args, **kwargs, and type hints.

R
by Rupa
Apr 5, 20255 min read

Control Flow

control_flow.py
Loading editor...

match Statement (Python 3.10+)

Python's match is like switch but far more powerful — it does structural pattern matching:

command = "quit"

match command:
    case "quit":
        print("Quitting...")
    case "help":
        print("Commands: quit, help, start")
    case "start" | "run":       # multiple patterns
        print("Starting...")
    case _:                      # default
        print(f"Unknown command: {command}")

# Match on types and structure
def process(value):
    match value:
        case int(n) if n > 0:
            return f"Positive int: {n}"
        case str(s) if len(s) > 0:
            return f"Non-empty string: {s!r}"
        case [first, *rest]:
            return f"List starting with {first}, {len(rest)} more"
        case {"name": name, "age": age}:
            return f"Dict: {name}, age {age}"
        case None:
            return "Got None"
        case _:
            return "Something else"

print(process(42))
print(process("hello"))
print(process([1, 2, 3]))
print(process({"name": "Rupa", "age": 25}))

Loops

for Loop and range()

# range(stop)
for i in range(5):
    print(i)  # 0 1 2 3 4

# range(start, stop, step)
for i in range(2, 11, 2):
    print(i)  # 2 4 6 8 10

# Iterate directly over a collection
names = ["Alice", "Bob", "Charlie"]
for name in names:
    print(name)

# enumerate — when you need both index and value
for i, name in enumerate(names, start=1):
    print(f"{i}. {name}")
# 1. Alice
# 2. Bob
# 3. Charlie

# zip — iterate two collections together
scores = [95, 87, 92]
for name, score in zip(names, scores):
    print(f"{name}: {score}")

while Loop

# Basic while
n = 10
total = 0
while n > 0:
    total += n
    n -= 1
print(total)  # 55

# while with else — runs if loop finished without break
attempts = 0
while attempts < 3:
    password = input("Password: ")  # remove this in code editor
    if password == "secret":
        print("Access granted")
        break
    attempts += 1
else:
    print("Too many failed attempts")

break, continue, pass

# break — exit the loop
for n in range(10):
    if n == 5:
        break
    print(n)  # 0 1 2 3 4

# continue — skip this iteration
for n in range(10):
    if n % 2 == 0:
        continue
    print(n)  # 1 3 5 7 9

# pass — do nothing (placeholder)
for n in range(5):
    if n == 3:
        pass   # TODO: handle this case later
    print(n)

# for/else — runs if loop completed without break
for n in range(2, 10):
    for factor in range(2, n):
        if n % factor == 0:
            break
    else:
        print(f"{n} is prime")  # 2 3 5 7

Functions

functions.py
Loading editor...

Return Values

# Return multiple values (actually returns a tuple)
def min_max(numbers: list[int]) -> tuple[int, int]:
    return min(numbers), max(numbers)

low, high = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(f"Min: {low}, Max: {high}")  # Min: 1, Max: 9

# Implicit return None
def log(message: str) -> None:
    print(f"[LOG] {message}")
    # no return statement — returns None automatically

Default Arguments

def create_user(
    name: str,
    role: str = "user",
    active: bool = True,
    tags: list[str] | None = None,  # ← correct pattern
) -> dict:
    return {
        "name": name,
        "role": role,
        "active": active,
        "tags": tags or [],
    }

print(create_user("Alice"))
print(create_user("Bob", role="admin"))
print(create_user("Eve", active=False, tags=["beta"]))
Never use mutable defaults
# ❌ Bug — the list is shared across ALL calls
def append_item(item, lst=[]):
    lst.append(item)
    return lst

print(append_item(1))  # [1]
print(append_item(2))  # [1, 2]  ← NOT what you expected

# ✅ Correct — use None and create inside the function
def append_item(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

This is one of Python's most common beginner bugs.

Lambda Functions

# Small anonymous functions
double = lambda x: x * 2
add    = lambda x, y: x + y

print(double(5))   # 10
print(add(3, 4))   # 7

# Most useful with sorted(), map(), filter()
products = [
    {"name": "Tablet", "price": 399},
    {"name": "Laptop", "price": 999},
    {"name": "Phone",  "price": 599},
]

sorted_by_price = sorted(products, key=lambda p: p["price"])
for p in sorted_by_price:
    print(f"  {p['name']}: ${p['price']}")

# filter and map
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda n: n % 2 == 0, numbers))
doubled = list(map(lambda n: n * 2, numbers))
# Use list comprehensions instead — they're more Pythonic:
evens   = [n for n in numbers if n % 2 == 0]
doubled = [n * 2 for n in numbers]

Scope — LEGB Rule

Python resolves names in this order: Local → Enclosing → Global → Built-in:

x = "global"

def outer():
    x = "enclosing"

    def inner():
        x = "local"
        print(x)   # local

    inner()
    print(x)       # enclosing

outer()
print(x)           # global

# global and nonlocal keywords
counter = 0
def increment():
    global counter      # modify the global variable
    counter += 1

def make_counter():
    count = 0
    def inc():
        nonlocal count  # modify the enclosing variable
        count += 1
        return count
    return inc

c = make_counter()
print(c())  # 1
print(c())  # 2

What's Next?

Python #4 covers Lists, Tuples, Sets, and Dictionaries — the four core collection types, when to use each, and comprehensions.

#python#basics#functions#control-flow#loops

✦ Enjoyed this post?

Get posts like this in your inbox

No spam, just real tutorials when they're ready.

Discussion

Powered by GitHub

Comments use GitHub Discussions — no separate account needed if you have GitHub.