Conditional Logic in Methods
Real data has gaps. A method needs to behave sensibly when the collection is empty, a value is missing, or a search finds nothing at all.
A method that behaves differently depending on what it finds.
1. Learning Objective
By the end of this session you will be able to:
- Write a method that branches based on the state of the instance
- Use a ternary (conditional) expression for a short either/or value
- Handle an empty list gracefully without a crash or a misleading result
- Use the "truthy" nature of empty lists/strings in an if condition
- Explain the difference between "no result found" and "an actual error"
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 total_population(self):\n return sum(c.population for c in self.countries), what does this return if self.countries is an empty list?
sum() of an empty sequence returns 0 by definition โ no special handling is needed here. This is worth confirming explicitly rather than assuming.What does if self.countries: check, given that self.countries is a list?
if self.countries: is idiomatic for "does this list have anything in it?" โ equivalent to but more idiomatic than checking length explicitly.Given label = "Africa" if country.region == "Africa" else "Other", what kind of expression is this?
value_if_true if condition else value_if_false. It is an expression (it produces a value) rather than a statement, useful for short either/or assignments.A method find_by_region(region) returns [] when no country matches. Is returning an empty list the same kind of situation as raising an exception?
Given a method that must show a message when there are no countries, which is more idiomatic Python?
if not self.countries: relies on the truthy/falsy behaviour from this session and is the idiomatic Python style. The other two work but are unnecessarily verbose for the same check.3. The Concept โ Conditional Logic in Methods
Empty collections are falsy; anything with at least one item is truthy.
Branching based on instance state
A method can look at self's current attributes and behave differently depending on what it finds โ exactly like the plain if-statements from earlier sessions, just now reading self instead of a passed-in argument.
class CountryExplorer:
def __init__(self, countries):
self.countries = countries
def status_message(self):
if not self.countries:
return "No countries loaded yet."
return f"{len(self.countries)} countries loaded."
empty_explorer = CountryExplorer(countries=[])
print(empty_explorer.status_message()) # "No countries loaded yet."
Truthy and falsy collections
Python treats an empty list, empty string, empty dict, 0, and None as falsy in a boolean context. A non-empty version of any of them is truthy. This lets you write if not self.countries: instead of the more verbose if len(self.countries) == 0:.
countries = []
print(bool(countries)) # False โ empty list is falsy
countries = [1]
print(bool(countries)) # True โ non-empty list is truthy
# Idiomatic style
if not countries:
print("empty")
else:
print("has items")
The ternary expression for short either/or values
When a value simply needs to be one of two things based on a condition, a ternary expression is more compact than a full if/else block, and it produces a value directly rather than a side effect.
def region_or_unknown(country):
return country.region if country.region else "Unknown"
# equivalent longhand
def region_or_unknown_longhand(country):
if country.region:
return country.region
else:
return "Unknown"
"Not found" is not the same as "broken"
Recall Session 11: exceptions are for genuinely invalid states. A search that legitimately finds nothing should return an empty, valid result โ not raise an exception. Confusing the two forces every caller to wrap normal searches in try/except unnecessarily.
class CountryExplorer:
def __init__(self, countries):
self.countries = countries
def find_by_region(self, region):
# Zero matches is a NORMAL outcome โ return an empty list, don't raise
return [c for c in self.countries if c.region == region]
explorer = CountryExplorer(countries=[])
result = explorer.find_by_region("Antarctica")
print(result) # [] โ a valid, empty answer, no crash, no exception
4. Lab
What you will build
A file called conditional_lab.py, building on Session 16's CountryExplorer.
Step-by-step instructions
Create the file with Country and CountryExplorer
# conditional_lab.py
class Country:
def __init__(self, name, region, population):
self.name = name
self.region = region
self.population = population
def summary(self):
return f"{self.name} ({self.region}): pop. {self.population:,}"
class CountryExplorer:
def __init__(self, countries):
self.countries = countries
Add a status_message() method using not self.countries
class CountryExplorer:
def __init__(self, countries):
self.countries = countries
def status_message(self):
if not self.countries:
return "No countries loaded yet."
return f"{len(self.countries)} countries loaded."
empty = CountryExplorer(countries=[])
loaded = CountryExplorer(countries=[Country(name="Kenya", region="Africa", population=54000000)])
print(empty.status_message())
print(loaded.status_message())
Add find_by_region() and confirm zero matches returns [] without a crash
class CountryExplorer:
def __init__(self, countries):
self.countries = countries
def status_message(self):
if not self.countries:
return "No countries loaded yet."
return f"{len(self.countries)} countries loaded."
def find_by_region(self, region):
return [c for c in self.countries if c.region == region]
explorer = CountryExplorer(countries=[Country(name="Kenya", region="Africa", population=54000000)])
result = explorer.find_by_region("Antarctica")
print(result) # []
print(type(result)) # <class 'list'> โ never None, never an exception
Add a ternary-based method for a country's display capital
Give Country an optional capital, defaulting to None, and add a method that returns it or "Unknown".
class Country:
def __init__(self, name, region, population, capital=None):
self.name = name
self.region = region
self.population = population
self.capital = capital
def display_capital(self):
return self.capital if self.capital else "Unknown"
k = Country(name="Kenya", region="Africa", population=54000000, capital="Nairobi")
u = Country(name="Unclaimed", region="Antarctica", population=0)
print(k.display_capital()) # "Nairobi"
print(u.display_capital()) # "Unknown"
Confirm status_message on an explorer with one country added after construction
explorer = CountryExplorer(countries=[])
print(explorer.status_message()) # "No countries loaded yet."
explorer.countries.append(k)
print(explorer.status_message()) # "1 countries loaded." โ note the grammar, discuss in reflection
5. Expected Files Changed
| File | Action | Why |
|---|---|---|
conditional_lab.py |
Created | Adds conditional branching, truthy checks, and a ternary display method. |
docs/sessions/session-17/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 conditional_lab.py docs/sessions/session-17/index.html
git commit -m "session-17: handle empty collections and missing values with conditional logic"
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 total_population(self):\n return sum(c.population for c in self.countries), what does this return if self.countries is an empty list?
sum() of an empty sequence returns 0 by definition โ no special handling is needed here. This is worth confirming explicitly rather than assuming.What does if self.countries: check, given that self.countries is a list?
if self.countries: is idiomatic for "does this list have anything in it?" โ equivalent to but more idiomatic than checking length explicitly.Given label = "Africa" if country.region == "Africa" else "Other", what kind of expression is this?
value_if_true if condition else value_if_false. It is an expression (it produces a value) rather than a statement, useful for short either/or assignments.A method find_by_region(region) returns [] when no country matches. Is returning an empty list the same kind of situation as raising an exception?
Given a method that must show a message when there are no countries, which is more idiomatic Python?
if not self.countries: relies on the truthy/falsy behaviour from this session and is the idiomatic Python style. The other two work but are unnecessarily verbose for the same check.9. Reflection Questions
Think through these after the post-quiz. No right answer โ they are for discussion.
- Step 5 printed "1 countries loaded." โ grammatically wrong. How would you fix this with a ternary expression? Try writing it.
- Why is returning an empty list from find_by_region a better design than returning None when nothing is found?
- Can you think of a case in this lab where using an exception (Session 11) WOULD have been the right choice instead of a normal empty return?
- How does the truthy/falsy behaviour of empty lists connect to what you learned about mutability and identity in Session 05 and 02?
10. What Breaks If This Knowledge Is Missing?
- None-checking chaos: If find_by_region sometimes returned None instead of an empty list, every single caller would need an extra None-check before it could safely loop over the result โ multiplying defensive code throughout the whole project for no benefit.
- Building lists of objects (Session 18): The next session focuses on constructing many Country instances at once, often from raw data that may have missing fields. This session's conditional patterns are what keep that construction from crashing on incomplete records.
- UI-equivalent states (parallel to the original React course): This is directly analogous to Session 17 of the source React course ("Conditional Rendering") โ handling an empty search result gracefully is the same problem whether you are printing to a console or rendering to a screen.
11. What We Learned
Python concept mastered: Conditional logic inside methods โ truthy/falsy checks on collections, ternary expressions, and treating "not found" as a normal outcome rather than an error.
Unlocks: CountryExplorer now handles the messy, incomplete cases real data throws at it, without crashing or producing misleading results.
Next session: Session 18 โ Building Lists of Objects. We build lists of Country objects from raw data at scale, and formalize the pattern for converting many dicts into many instances.