EAFP, Truthiness, and the Mutable Default Trap
This lesson collects three idioms that separate beginner code from mid-tier code. The last one is a real bug that bites almost everyone once.
EAFP vs LBYL
Python prefers EAFP: Easier to Ask Forgiveness than Permission. Instead of checking first (Look Before You Leap), you try the operation and catch the failure:
# LBYL (more brittle)
if "age" in data:
age = data["age"]
else:
age = 0
# EAFP (pythonic)
try:
age = data["age"]
except KeyError:
age = 0
# or just
age = data.get("age", 0)Truthiness
Empty containers, 0, "", and None are all falsy. So you rarely compare to them explicitly:
# instead of: if len(items) > 0:
if items:
print("has items")
# instead of: if name != "":
if name:
print("has a name")The mutable default argument trap
A default argument is evaluated once, when the function is defined, not each time it is called. So a mutable default like [] is shared across every call. Watch this go wrong:
# BUG: the list persists between calls
def add_item(item, bucket=[]):
bucket.append(item)
return bucket
add_item("a") # ['a']
add_item("b") # ['a', 'b'] -- not what you wanted!The fix is the standard None sentinel pattern: default to None, then create a fresh list inside:
def add_item(item, bucket=None):
if bucket is None:
bucket = []
bucket.append(item)
return bucketNow each call with no bucket starts fresh.
Fix the trap. Write add_item(item, bucket=None) that appends item to bucket and returns it, but creates a brand new list when no bucket is passed, so separate calls do not share state. Use the if bucket is None: pattern.
This lesson is locked
Lessons open one at a time. Finish the previous lesson to unlock this one.