Removing Redundant Conditions

Concepts covered: Boolean Operators

A condition is redundant when deleting it changes nothing the program does. For every value that can reach the line, the same branch runs, the same variables end with the same values, and the same exceptions are raised or avoided. Redundant checks cost the next reader time. Someone who sees amount < 1000 on a branch assumes an amount of 1000 or more can arrive there, and goes looking for a case that never comes.

Facts an elif already knows

An elif test runs only after every test above it came out False. Each of those False results is a fact the branch gets for free, and you can delete a check that restates one of them.

if amount >= 1000:
    tier = "large"
elif amount >= 100 and amount < 1000:
    tier = "medium"
elif amount >= 0 and amount < 100:
    tier = "small"
else:
    tier = "invalid"

By the second test, amount >= 1000 has already failed, so amount < 1000 is True every time Python reaches it. By the third test, amount >= 100 has failed as well, which guarantees amount < 100. Both halves can go, and the chain becomes 3 cut points:

if amount >= 1000:
    tier = "large"
elif amount >= 100:
    tier = "medium"
elif amount >= 0:
    tier = "small"
else:
    tier = "invalid"

The reasoning says the rewrite is safe, and boundary values confirm it. Run both versions on each value where a comparison flips, taking 1 step either side of every cut point:

amountoriginal tiersimplified tier
-1invalidinvalid
0smallsmall
99smallsmall
100mediummedium
999mediummedium
1000largelarge

3 more shapes of a dead check

Checks that only look redundant

Short-circuit evaluation gives some checks a second job. In fee is not None and fee > 0, Python evaluates the right test only when the left one is True. If you delete the left test, a None fee no longer produces a quiet False. It reaches fee > 0 and raises:

fee = None
print(fee is not None and fee > 0)
try:
    print(fee > 0)
except TypeError as err:
    print("TypeError:", err)

Output:

False
TypeError: '>' not supported between instances of 'NoneType' and 'int'

Now compare fee is not None and fee with plain fee as the test of an if. None is falsy, so fee alone already sends it down the False branch, and nothing on the right can raise. In that position the None test is redundant. Stored in a variable, the 2 forms differ: for a None fee, the first evaluates to False and the second to None, so deleting the test changes a value. The same test is needed in one place and redundant in another, so every removal needs its own argument.

A related look-alike: if is_refund == True: and if is_refund: agree only when is_refund always holds a bool. 1 == True is True, but "yes" == True is False even though "yes" is truthy. The shorter test would let through strings that the longer one refused.

Which part of the third test can be deleted without changing status for any value of rows, including None?

if rows is None:
    status = "missing"
elif rows == 0:
    status = "empty"
elif rows is not None and rows > 0:
    status = "loaded"
else:
    status = "corrupt"

In that question the guard was dead because an earlier branch had already taken every None. Put no None branch above it, and the same words become the only thing between a None value and <. Whether a guard is removable depends on what sits above it, never on the guard's own wording.

About this lesson

This section is part of the Control Flow: Advanced lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.