Python EAFP vs LBYL, explained
EAFP — "Easier to Ask Forgiveness than Permission" — and LBYL — "Look Before You Leap" — are the two strategies for dealing with operations that might fail. Python culture leans heavily toward EAFP, and knowing why reveals something deep about how the language is meant to be used.
LBYL: check, then act
LBYL guards an operation with explicit tests beforehand. It reads naturally but has subtle problems in Python.
# LBYL
if "name" in user and user["name"] is not None:
name = user["name"]
else:
name = "anonymous"
You check the precondition, then perform the action. The trouble is the checks can become a thicket, and — more seriously — the world can change between the check and the action.
EAFP: just try it
EAFP assumes the operation will succeed and handles the exception if it doesn't. It's the Pythonic default.
# EAFP
try:
name = user["name"]
except KeyError:
name = "anonymous"
This expresses intent directly: "get the name; if there isn't one, fall back." There's no duplicated key lookup and no precondition logic to keep in sync with the action.
Why EAFP is usually preferred
Three reasons. First, no time-of-check/time-of-use race: with LBYL, a file can be
deleted between os.path.exists and open. Second, it's often faster on the happy path
— exceptions are cheap when they don't fire, and you skip a redundant check. Third, it
handles cases the check might miss (a value that's present but wrong type).
# LBYL has a race condition:
if os.path.exists(path):
open(path) # file may be gone by now → crash
# EAFP has none:
try:
open(path)
except FileNotFoundError:
handle_missing()
Duck typing and EAFP go together
Python's duck typing — "if it quacks like a duck" — is EAFP applied to types. Rather than checking an object's type, try the operation and let it fail if unsupported.
# Un-Pythonic LBYL on types:
if isinstance(x, (list, tuple)):
process(x)
# EAFP / duck typing:
try:
iter(x) # works for any iterable, not just list/tuple
except TypeError:
raise ValueError("need an iterable")
This makes code work with any type that supports the behaviour, not just the ones you anticipated.
When LBYL is the better choice
EAFP isn't dogma. Prefer LBYL when the check is cheap and the failure is expensive or common — for example validating user input before a costly operation, or when an exception mid-operation would leave state half-changed.
# LBYL is clearer here — validate before doing irreversible work
if not 0 <= percent <= 100:
raise ValueError("percent out of range")
apply_discount(percent)
Also use LBYL when an exception would be expected control flow hundreds of times per
second — a simple in check avoids the exception overhead in a hot loop.
Use the right built-in helper
Often neither raw style is needed — Python provides idioms that bake in EAFP. dict.get
with a default, getattr with a fallback, and collections.defaultdict express "try, else
default" without a try block at all.
name = user.get("name", "anonymous") # cleaner than try/except KeyError
val = getattr(obj, "attr", None)
Recap
EAFP (try the operation, catch the exception) is the Pythonic default: it avoids
time-of-check/time-of-use races, is fast on the happy path, and pairs naturally with duck
typing — try the behaviour instead of checking the type. LBYL (check first) is better
when the check is cheap and failure is expensive, common, or would corrupt state mid-way.
And often the cleanest answer is a built-in like dict.get, getattr, or defaultdict
that expresses "try, else default" with no try block at all.