Python
Error Handling
Conditional Statements
Programming Best Practices
Python Tips

Using 'try' vs. 'if' in Python

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The choice between try and if in Python is about uncertainty and intent, not style preference. if is better for known business conditions, while try is better for operations that may fail due to runtime state outside your control. Using the right tool makes code easier to read, safer under concurrency, and simpler to debug.

Use if for Domain Rules

When you can evaluate a rule deterministically, if communicates intent clearly.

python
1def withdraw(balance: float, amount: float) -> float:
2    if amount <= 0:
3        raise ValueError("amount must be positive")
4    if amount > balance:
5        raise ValueError("insufficient funds")
6    return balance - amount
7
8print(withdraw(100.0, 30.0))

This is business logic branching, not exception recovery.

Use try for Runtime-Uncertain Operations

Some failures cannot be prevented by pre-checks, such as filesystem changes, network issues, or concurrent state mutations.

python
1def read_counter(path: str) -> int:
2    try:
3        with open(path, "r", encoding="utf-8") as f:
4            return int(f.read().strip())
5    except FileNotFoundError:
6        return 0
7    except ValueError:
8        return 0

Checking file existence first is still vulnerable to race conditions if file disappears between check and read.

EAFP and LBYL in Python Terms

Two common styles:

  • EAFP, easier to ask forgiveness than permission.
  • LBYL, look before you leap.

EAFP example:

python
1def get_user_port(config: dict) -> int:
2    try:
3        return int(config["port"])
4    except KeyError:
5        return 8080
6    except ValueError:
7        return 8080

LBYL example:

python
1def get_user_port(config: dict) -> int:
2    if "port" not in config:
3        return 8080
4    value = config["port"]
5    return int(value) if str(value).isdigit() else 8080

Both are valid. Choose based on readability and expected failure frequency.

Keep try Scope Narrow

Broad try blocks can hide unrelated bugs and make diagnostics unclear.

Too broad:

python
1def process(path: str) -> int:
2    try:
3        content = open(path, "r", encoding="utf-8").read()
4        values = [int(x) for x in content.split(",")]
5        return sum(values)
6    except Exception:
7        return 0

Better:

python
1def process(path: str) -> int:
2    try:
3        with open(path, "r", encoding="utf-8") as f:
4            content = f.read()
5    except FileNotFoundError:
6        return 0
7
8    try:
9        values = [int(x) for x in content.split(",")]
10    except ValueError:
11        return 0
12
13    return sum(values)

Specific exceptions and narrow blocks keep failures interpretable.

Avoid Catching Exception Without Purpose

Catching broad Exception can mask programming mistakes that should fail loudly.

Prefer specific catches and contextual logs:

python
1import logging
2
3logger = logging.getLogger(__name__)
4
5
6def parse_id(raw: str) -> int | None:
7    try:
8        return int(raw)
9    except ValueError:
10        logger.warning("invalid id: %s", raw)
11        return None

This preserves observability without swallowing unrelated defects.

Performance and Hot Loops

Exceptions are fine for uncommon failures, but frequent exception paths can be expensive and noisy.

In hot loops where misses are normal, direct checks are usually clearer.

python
1def count_hits(keys: list[str], table: dict[str, int]) -> int:
2    total = 0
3    for k in keys:
4        total += table.get(k, 0)
5    return total

Profile your actual workload before optimizing style choices.

Decision Checklist

Choose if when:

  • Condition is a core business rule.
  • Check is deterministic and cheap.
  • Branching is part of normal flow.

Choose try when:

  • Operation depends on external runtime state.
  • Race conditions can invalidate pre-checks.
  • Failure mode is expected and recoverable.

This checklist keeps codebase conventions consistent.

Common Pitfalls

  • Using if prechecks for operations that can still fail at execution time. Fix by wrapping risky operation in try.
  • Writing very broad try blocks. Fix by narrowing block to one risky statement group.
  • Catching Exception and hiding real bugs. Fix by catching specific exception types.
  • Using exceptions for normal frequent branches in tight loops. Fix by using direct checks for common miss cases.
  • Treating EAFP versus LBYL as doctrine. Fix by selecting based on readability and failure model.

Summary

  • 'if is best for explicit domain rules.'
  • 'try is best for runtime-uncertain operations.'
  • Narrow try scopes and specific exception types improve maintainability.
  • Frequent miss paths often favor direct checks over exception-driven flow.
  • Choose style by failure model and clarity, not by habit.

Course illustration
Course illustration

All Rights Reserved.