Functions and Lambda
Every piece of logic we have written so far has been repeated inline. Functions let us name a piece of logic once and reuse it โ the basis for everything from here forward.
Logic, named once, reusable everywhere.
1. Learning Objective
By the end of this session you will be able to:
- Define a function with def, including default parameter values
- Explain the difference between a parameter and an argument, and between positional and keyword arguments
- Use return to send a value back to the caller, and understand a function with no return gives back None
- Write a small lambda expression and know when it is (and is not) appropriate
- Pass a function as an argument to sorted() using the key parameter
2. Pre-Coding Quiz
Answer these before reading the concept explanation or writing any code. It is fine to get these wrong โ that is the point. You need 4/5 to proceed to the lab.
Given def greet(name):\n return f"Hello, {name}", what does greet("Kenya") evaluate to?
name, and return sends the formatted string back to the caller.What does print(greet("Kenya")) print if greet uses print(f"Hello, {name}") instead of return?
return, calling it evaluates to None โ and the outer print() then prints that None on the next line. This is a very common beginner confusion between printing and returning.Given def region_label(name, region="Unknown"):, what does region_label("Kenya") return for region?
Which lambda is equivalent to def get_pop(c): return c["population"]?
def, no name, no parentheses around parameters, and no return keyword โ the expression after the colon is implicitly returned.You call sorted(countries, key=lambda c: c["population"]). What does the key argument control?
sorted() normally compares items directly, which fails for dicts. key tells it: "for each item, run this function, and sort based on what it returns" โ here, each country's population.3. The Concept โ Functions and Lambda
print() sends text to the console; return sends a value back into the calling code so it can be used further.
Defining a function
A function packages up logic under a name so it can be called repeatedly instead of copy-pasted.
def greet(name):
return f"Hello, {name}"
print(greet("Kenya")) # "Hello, Kenya"
print(greet("Ghana")) # "Hello, Ghana"
return vs print โ a critical distinction
print() displays something to the console as a side effect. return sends a value back to whoever called the function, so it can be stored, passed along, or used in another expression. A function with no return statement evaluates to None.
def broken_greet(name):
print(f"Hello, {name}") # side effect โ does NOT return anything
result = broken_greet("Kenya") # prints "Hello, Kenya" as a side effect
print(result) # None โ nothing was returned
Default parameter values
A parameter can have a default, used whenever the caller omits that argument.
def region_label(name, region="Unknown"):
return f"{name} ({region})"
print(region_label("Kenya", "Africa")) # "Kenya (Africa)"
print(region_label("Atlantis")) # "Atlantis (Unknown)" โ default used
Lambda โ a small, anonymous function
A lambda is a function expression with no name, written on one line, with the result implicitly returned. Lambdas are for short, throwaway logic โ usually passed straight into another function.
# Full function
def get_pop(c):
return c["population"]
# Equivalent lambda
get_pop = lambda c: c["population"]
# Where lambdas actually shine: passed inline as an argument
countries = [
{"name": "Kenya", "population": 54000000},
{"name": "Ghana", "population": 31000000},
]
by_population = sorted(countries, key=lambda c: c["population"])
print([c["name"] for c in by_population]) # ['Ghana', 'Kenya']
When to use a full function instead of a lambda
If the logic needs a name for clarity, spans more than one line, or is reused in several places, write a real def function. A lambda is for a single, obvious, inline expression only.
# Bad: cramming multi-step logic into a lambda hurts readability
# label = lambda c: c["name"] + " (" + c["region"] + ")" if c.get("region") else c["name"]
# Good: a real function with a name says what it does
def format_label(c):
if c.get("region"):
return f"{c['name']} ({c['region']})"
return c["name"]
4. Lab
What you will build
A file called functions_lab.py.
Step-by-step instructions
Create the file and the country list
# functions_lab.py
countries = [
{"name": "Kenya", "region": "Africa", "population": 54000000},
{"name": "Ghana", "region": "Africa", "population": 31000000},
{"name": "Peru", "region": "Americas", "population": 33000000},
]
Write a function that formats one country as a label
Give region a default value of "Unknown".
def format_country(name, region="Unknown"):
return f"{name} ({region})"
print(format_country("Kenya", "Africa"))
print(format_country("Atlantis")) # uses the default
Write a function that returns the population, and one that prints it
Call both and print the results to see the return-vs-print difference for yourself.
def get_population(country):
return country["population"]
def show_population(country):
print(country["name"], "has population", country["population"])
result = show_population(countries[0])
print("show_population returned:", result) # None
print("get_population returned:", get_population(countries[0]))
Sort countries by population using a lambda
by_population = sorted(countries, key=lambda c: c["population"])
for c in by_population:
print(c["name"], c["population"])
Sort countries by population, descending
Look up the reverse parameter of sorted().
by_population_desc = sorted(countries, key=lambda c: c["population"], reverse=True)
print([c["name"] for c in by_population_desc])
5. Expected Files Changed
| File | Action | Why |
|---|---|---|
functions_lab.py |
Created | Demonstrates function definitions, defaults, return vs print, and lambda with sorted(). |
docs/sessions/session-08/index.html |
Created | This session document. |
6. Commit Checkpoint
Once the lab is complete and you can explain every line, make this exact commit:
git add functions_lab.py docs/sessions/session-08/index.html
git commit -m "session-08: define reusable functions and sort with a lambda key"
7. Code Review Checklist
Go through your code line by line and check each item:
8. Post-Coding Quiz
Same 5 questions. Take it again now that you have written and run the code. You need 4/5 to mark this session complete.
Given def greet(name):\n return f"Hello, {name}", what does greet("Kenya") evaluate to?
name, and return sends the formatted string back to the caller.What does print(greet("Kenya")) print if greet uses print(f"Hello, {name}") instead of return?
return, calling it evaluates to None โ and the outer print() then prints that None on the next line. This is a very common beginner confusion between printing and returning.Given def region_label(name, region="Unknown"):, what does region_label("Kenya") return for region?
Which lambda is equivalent to def get_pop(c): return c["population"]?
def, no name, no parentheses around parameters, and no return keyword โ the expression after the colon is implicitly returned.You call sorted(countries, key=lambda c: c["population"]). What does the key argument control?
sorted() normally compares items directly, which fails for dicts. key tells it: "for each item, run this function, and sort based on what it returns" โ here, each country's population.9. Reflection Questions
Think through these after the post-quiz. No right answer โ they are for discussion.
- Why does Python separate "produce a value" (return) from "display a value" (print) instead of combining them?
- Rewrite the
lambda c: c["population"]from the lab as a fulldeffunction. Which version is more readable in context? - What would happen if you forgot the
returnkeyword insideget_population? Try it and observe. - Can you think of a case in the project ahead where a default parameter value would prevent a bug?
10. What Breaks If This Knowledge Is Missing?
- The forgotten-return bug: Forgetting
returnis one of the most common beginner mistakes. The function appears to work (it prints correctly) but every caller that tries to use its result getsNoneinstead โ a bug that only shows up downstream. - Data-layer functions (Layer 4): Session 28 builds a data-access layer entirely out of functions like the ones in this lab. If return values are not understood, that entire layer will silently pass
Nonearound. - Sorting and filtering UI logic: Nearly every "sort by X" or "filter by Y" feature in real software is built on exactly the
key=lambdapattern from this session.
11. What We Learned
Python concept mastered: Functions โ def, parameters, defaults, return vs print, and lambda expressions used as sort keys.
Unlocks: You can now name and reuse logic instead of repeating it, and pass small functions as arguments to other functions.
Next session: Session 09 โ Unpacking and *args/**kwargs. We will learn to unpack values out of lists and dicts in one line, and accept flexible numbers of arguments with *args and **kwargs.