04. Conditional Statements

A practical guide to Python conditional logic, including branching, Boolean expressions, validation, guard clauses, and common decision-making errors.

How Conditional Logic Controls Execution

A program normally executes statements in sequence, but decision logic allows it to choose which instructions should run. A evaluates a condition and follows the branch appropriate to the result.

In Python, conditions commonly produce True or False. Other values can also be tested: values such as 0, None, and empty collections are generally treated as false, while many nonempty or nonzero values are treated as true.

A useful mental model is:

  1. Evaluate the condition.

  2. Select the first applicable branch.

  3. Execute that branch’s indented statements.

  4. Continue after the complete conditional structure.

Takeaway: Conditional logic changes a program from a fixed sequence into a set of choices controlled by data.

The Basic `if` Decision

An if statement is the basic one-way decision. It runs an indented block only when its condition is true.

Example: if age >= 18: print("Adult") prints the message when age is at least 18. The colon marks the start of the block, and indentation identifies the statements belonging to it.

The general pattern is:

  • if condition:

  • Indented statements to execute when the condition is true

If the condition is false, Python skips the indented block and continues with the statement after the conditional. The block can contain assignments, function calls, loops, or another conditional structure.

Takeaway: Use if when an action is optional and should occur only after a condition succeeds.

Choosing Between Two Alternatives

When exactly one of two alternatives must run, add else to an if statement. The first branch runs when the condition is true; otherwise, the else branch runs. Exactly one of the two branches executes.

For example, a temperature rule can be expressed as follows:

  • If temperature > 20, print "Wear light clothing".

  • Otherwise, print "Wear a jacket".

An else branch has no condition of its own. It represents every case not accepted by the preceding if condition. This makes it useful for a default action, fallback result, or general failure message.

Takeaway: Use if with else when the decision has two mutually exclusive outcomes.

Building Ordered Decision Chains

For more than two possible outcomes, use elif, which means “else if.” Python tests the conditions from top to bottom and executes only the first branch whose condition is true. Any later conditions are skipped.

A grade classifier might test score >= 90, then score >= 80, then score >= 70, and so on. A score of 76 reaches the third matching range and receives "C".

Branch order matters. A broad condition such as score >= 60 must not appear before a more specific condition such as score >= 90, because the broad branch would capture scores that should receive the more specific result. Arrange branches from the highest priority or most specific case to the most general case.

A structure may contain zero or more elif branches and an optional else branch.

Takeaway: In an ordered chain, the first true branch wins, so write and review branch order deliberately.

Combining Conditions with Boolean Logic

Conditions often use comparison operators:

  • == checks whether two values are equal.

  • != checks whether two values are different.

  • < and > test whether one value is smaller or larger.

  • <= and >= include equality at the boundary.

Boolean operators combine simpler tests:

  • and is true only when both conditions are true.

  • or is true when at least one condition is true.

  • not reverses a Boolean result.

For example, access can require both age >= 18 and has_ticket. Another rule might allow access when (is_member and paid) or has_guest_pass. Parentheses make the intended grouping clear, especially when and and or appear together.

A should express the rule the program actually needs to enforce. Break a complicated rule into meaningful parts when that improves readability.

Takeaway: Combine small, understandable tests to express more complex rules, and use parentheses when grouping could be misunderstood.

Managing Dependent Decisions

A nested condition places one decision inside the branch of another. This is appropriate when the second decision should be considered only after the first requirement succeeds.

For a login process, the program might first check that the username is not empty and then check whether the password is correct. The password test should not run as part of the successful path until the username requirement has been met.

Nesting can become difficult to read when many levels accumulate. A chained structure can often express the same outcomes more clearly:

  • If the username is empty, report that a username is required.

  • Otherwise, if the password is incorrect, report the password error.

  • Otherwise, report a successful login.

Related conditions can sometimes be combined with Boolean operators, or invalid cases can be handled first with a .

Takeaway: Nest only when decisions are genuinely dependent; otherwise, prefer a flatter and more readable structure.

Validating Data Before Use

checks data before the program relies on it. A dependable sequence usually follows this order:

  1. Check whether the input has the required basic form.

  2. Check whether its value is within the allowed range.

  3. Report the specific failure when a rule is not satisfied.

  4. Continue only after the input passes the necessary checks.

For an age value, the program can reject a negative number, reject a value above the expected maximum, and accept all remaining values. For a password, one condition can require at least eight characters while another checks that no spaces are present.

Check dangerous cases before performing dependent operations. For division, test whether the denominator is zero before calculating the quotient. This prevents the program from attempting an invalid operation.

When several rules can fail independently, distinct messages help users correct the relevant problem.

Takeaway: Validate early, check unsafe cases first, and make failure reasons useful.

Useful Branching Patterns

A handles an invalid or exceptional case immediately. Inside a function, it often uses return to finish that case before the main operation begins.

For example, a discount function can return "Invalid price" when the price is negative, return the original price when the customer is not a member, and calculate the discounted price only for a valid member purchase. This keeps the normal path at a low indentation level.

Chained conditions are useful for range classification. A speed classifier can return "Invalid" for negative values, "Slow" through 30, "Moderate" through 60, and "Fast" above 60. The boundaries must be intentional: a value of 30 belongs to the first permitted range in this example, while 31 belongs to the next.

A is suitable for a short two-value choice, such as status = "adult" if age >= 18 else "minor". Use a full block when either branch contains multiple statements or the compact form would reduce clarity.

Takeaway: Choose a branching pattern that keeps exceptional cases visible, boundaries explicit, and the main path easy to follow.

Avoiding Common Conditional Errors

Several errors appear frequently in conditional logic.

  • Assignment instead of comparison: Use == to compare values. A single = assigns a value to a variable.

  • Incorrect repeated test with or: choice == "yes" or "y" does not test both alternatives. The nonempty string "y" is truthy, so the condition effectively remains true. Compare explicitly with choice == "yes" or choice == "y", or use choice in ("yes", "y").

  • Incorrect branch order: Put specific or high-priority cases before broad cases. Otherwise, an early broad branch can prevent a later branch from ever running.

  • Boundary mistakes: For a rule such as age >= 18, test values 17, 18, and 19. Testing just below, exactly at, and just above each boundary exposes errors involving < versus <= or > versus >=.

When debugging, ask which condition is evaluated first, whether each operator expresses the intended rule, and whether every possible case reaches the correct branch.

Takeaway: Review operators, truthiness, branch order, and boundary values instead of testing only typical inputs.

Putting Conditional Statements Together

Python conditional logic is most reliable when each branch has a clear purpose and the conditions are checked in a deliberate order.

  • Use if for a one-way decision.

  • Add else for the alternative path.

  • Use elif for an ordered set of alternatives.

  • Combine conditions with and, or, and not when that is clearer than unnecessary nesting.

  • Validate data before using it in dependent operations.

  • Use guard clauses to handle invalid or exceptional cases early.

  • Check range boundaries and invalid inputs explicitly.

  • Choose a only for a compact, readable two-value choice.

The central design question is not merely whether a condition works, but whether another reader can understand why each branch exists and which cases it covers.