Why it matters
Often you do not need a whole block of code. You only want to pick one of two values, such as the word even or odd, the price 150 or 200, or the larger of two numbers. Writing that with an if and an else takes four or five lines, and each line repeats the variable you are setting. Python has a short form for this choice. It works as one expression, so you can place it directly inside a print, a return or a calculation.
The short form is its strength. It is also easy to overuse. This lesson shows when it makes code clearer, when it makes code harder to read, and the rules you must follow so the answer is always what you expect.
The idea: a choice that produces a value
Python calls this a conditional expression. Many programmers still call it the ternary operator, because it has three parts. Its shape is:
value_if_true if condition else value_if_falseRead it as English: "this value if the condition holds, otherwise that value." The condition is tested first. If it is true, the first value is the result. If it is false, the second value is the result. Only one of the two values is used, and the other is ignored.
Think of a road sign at a fork: "if the bridge is closed, take the high road, otherwise take the river road." You arrive at one road. The conditional expression also arrives at one value, and that value is the answer of the whole expression.
A short note for readers who know Java: the same choice is written condition ? a : b there. Python has no ? and no : for this job. The words if and else do the work, and the value you want when the test passes comes first.
The key word is expression. An expression produces a value, such as 3 + 4 producing 7. A statement does something, such as print(...). An if block is a statement: it runs code. The conditional expression is an expression: it gives back a value that you can store, print, return or pass to a function. That difference explains almost every rule in this lesson.
Here is a small program. It reads a number and prints whether it is even or odd. When the program asks for a number, type 7 and press Enter.
n = int(input())
kind = "even" if n % 2 == 0 else "odd"
print(f"{n} is {kind}")The output for 7 is:
7 is oddTrace it. n is 7. The condition n % 2 == 0 first computes 7 % 2, which is the remainder after dividing 7 by 2. That remainder is 1, so the comparison 1 == 0 is False. The expression therefore chooses the second value, "odd", and stores it in kind. The print on the next line then uses that stored value.
Now type -3. The output is -3 is odd, which is correct. For negative numbers, Python's remainder takes the sign of the divisor, so -3 % 2 is 1:
print(-3 % 2, -3 % 2 == 1)1 TrueBecause of this, both n % 2 == 0 and n % 2 == 1 give the right answer for negative numbers in Python. The == 0 test is still the clearer one for even, and it is the same test you would use in any other language.
The if-else version, side by side
The same choice written as an if and an else looks like this:
if n % 2 == 0:
kind = "even"
else:
kind = "odd"Both versions do the same work, with the same test and the same result. The conditional expression is shorter, and it has one more advantage: the variable is set in one place, so you cannot forget the else branch and leave kind unset. In the if version, an else is optional. Without one, kind is not set when the test is false, and the next line that reads it fails. The conditional expression cannot be half-written, because Python refuses the line without its else part:
kind = "even" if n % 2 == 0SyntaxError: expected 'else' after 'if' expressionUse the conditional expression when both branches produce a simple value and the choice is one line. Use the if block when a branch does real work, such as printing, saving or changing several variables. The next section shows why that rule exists.
Using the choice inside other code
Because the conditional expression produces a value, it can sit anywhere a value is expected. The most common places are inside an f-string, inside a return, and as an argument to a function.
The next program prints a correct word after a count. A count of 1 needs the singular item, and any other count needs items. When the program asks for a number, type 1, and then try 4 and 0.
count = int(input())
word = "item" if count == 1 else "items"
print(f"{count} {word}")The output for 1 is:
1 itemFor 4 the output is 4 items, and for 0 it is 0 items. English uses the plural for zero, so the rule count == 1 is exactly right. You could put the conditional expression inside the f-string itself, as shown later in this lesson. That works, but a separate variable is easier to read, especially when the expression grows.
Next is a function. A function is a named block of code that takes inputs and returns one value. The return line below is the entire body. The conditional expression chooses the word, and the function hands it back to the caller.
40: fail
70: passThis pattern is common in real code. A small function returns a label, a price or a flag, and the choice is a single line. The code calls verdict twice, and each call evaluates its own conditional expression.
Picking one of three values with a chain
You can put conditional expressions in a chain to choose among three outcomes. Python reads the chain from left to right in the test, and the else part groups to the right. Here is a sign label for a number:
zeroTrace it. x > 0 is False, so Python moves to the value after the first else, which is the second conditional expression "negative" if x < 0 else "zero". Its condition x < 0 is also False, so the result is "zero". Python reads the chain as "positive" if x > 0 else ("negative" if x < 0 else "zero"). You do not need the brackets, but a reader may find them helpful.
A chain of two is readable. A chain of three or more is not. The same logic is easier to follow as an if and elif ladder, because each test sits on its own line:
if x > 0:
sign = "positive"
elif x < 0:
sign = "negative"
else:
sign = "zero"elif is short for "else if". Choose the ladder when you have three or more choices, and keep the conditional expression for two-way choices or a single short chain. A good rule of thumb is that if you need to pause and count brackets to understand the expression, it should be an if block.
Clamping a value to a range
Clamping means forcing a value into a range. If the value is below the low end, it becomes the low end. If it is above the high end, it becomes the high end. Otherwise it stays as it is. Clamping is common in games, graphics, sliders, and in any program that must keep a value in a safe range. It is a natural use of a nested choice, because there are three outcomes.
The next function clamps a value, and the calls show the three cases. The first call is above the range, the second is below it, and the third is inside it.
10
0
5Trace clamp(15, 0, 10). First, 15 < 0 is False, so Python looks at the value after the first else, which is (high if value > high else value). Next, 15 > 10 is True, so the result is high, which is 10. For clamp(-3, 0, 10), the first test -3 < 0 is True, so the result is low, which is 0. The middle branch is never reached.
Python also has a built-in way to do the same job, min(max(value, low), high). Both versions give the same answer for a valid range. They differ when low is greater than high, which is an invalid range:
10 0The nested version returns low, and the built-in version returns high. Neither is a valid answer for a range that has no values, so the function should reject that input before it clamps. Clamping is a good place to test the three cases, low, middle and high, plus the two boundaries.
Types: the chosen value decides the type
Each branch keeps its own type. The chosen branch decides the type of the result, and the other branch is never looked at for its type.
1
<class 'int'>
<class 'float'>With flag set to True, the result is the integer 1, and it prints as 1, not 1.0. With flag set to False, the result is the float 2.5. In Python, you get whatever the chosen branch gives you.
That simplicity has a cost. Python does not check the two branches against each other, so a mismatch can pass unnoticed until the value is used:
<class 'int'>
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'int' and 'str'The assignment is accepted, because s gets the value 5. The error appears on the next line, where + is given an integer and a string. The traceback lines above the error name your file and the line, and they are omitted here. The error text is the part to read. A type checker, such as mypy, can report a mismatch like this before the program runs. Without one, check that both branches give values you can use the same way.
None and a failed operation
Python has a value called None, which means no value. A conditional expression can return None like any other value. The danger appears when a function is applied to None.
value 0
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'The first assignment is safe. The condition is True, so the expression chooses 0, and maybe + 1 is never evaluated. The second assignment chooses maybe + 1, which adds 1 to None, and Python stops with a TypeError. The rule is that a branch is evaluated only when it is chosen. When a value might be None, check it first, for example with maybe is not None, before you use it in the expression.
Where a conditional expression must not do work
The conditional expression chooses values. It is not meant to run actions. Python has no ++ operator, so the counting example from other languages needs a different form. Python does accept an expression on its own line, so this line runs and prints left:
leftThe line works, but it is hard to read. The value that print returns is None, and the conditional expression is thrown away. An if block says the same thing more clearly:
if flag:
print("left")
else:
print("right")A conditional expression that changes variables is also hard to read, even when it runs. Python lets you change a variable inside an expression with the walrus operator, :=, with brackets around it:
2 2 2Only a changed. A reader has to notice that from the condition and the brackets. In that case an if block with one line per branch is clearer. The rule is simple: a conditional expression should only compute a value, with no side effects.
What happens inside
Python evaluates a conditional expression in a fixed order. It tests the condition first. Then it evaluates one branch, and only that branch. The other branch is never run, so a function call in it does not happen.
1The division by zero in the second branch never runs, because the condition is True. The same rule explains the None example above, where the addition was safe in the first assignment. Keep this order in mind when a branch divides by something, because the division is safe only when the condition has already guarded it.
The condition does not need to be True or False exactly. Python tests whether a value counts as true. Empty things such as [], "", 0 and None count as false. Everything else counts as true:
no
yesThe list [0] is not empty, so it counts as true, even though it holds a zero. Use this rule with care. It is short and common in Python, but a reader who does not know it may read the condition wrongly.
A worked example: a delivery line
A shop shows a line on its basket page. If the order total is 50 or more, the line says Free delivery. Otherwise it shows the delivery charge. The program reads the total and the charge, then prints one line. The choice produces a single string, so a conditional expression fits well. When the program asks, type 32 5 and press Enter.
total, charge = map(int, input().split())
line = "Free delivery" if total >= 50 else f"Delivery charge: {charge}"
print(line)input() returns the whole typed line as text. .split() cuts that text at the spaces into two pieces, and map(int, ...) converts each piece to a whole number.
With 32 5, the output is:
Delivery charge: 5Trace it. total is 32, and 32 >= 50 is False. The expression chooses the second value, f"Delivery charge: {charge}". The f-string joins the text and the number to make Delivery charge: 5. The name line now holds that text, and print shows it.
The second value has to be a string. If you wrote "Delivery charge: " + charge instead, Python would stop with TypeError: can only concatenate str (not "int") to str, because + does not join a string and an integer. The f-string converts the number for you. The same mistake appears again in the common mistakes section.
Now type 50 5. The test is 50 >= 50, which is True, so the output is Free delivery. The boundary is included, because the rule says "50 or more". Type 49 5 and the output is Delivery charge: 5 again. Only the boundary value changes the outcome, and that is why a test set for this program must include 49, 50 and 51.
A small change shows the difference. If each branch needs more than one line, use an if block:
if total >= 50:
line = "Free delivery"
else:
line = "Delivery charge: " + str(charge)Both versions are correct. The conditional expression keeps the choice on one line next to the variable it sets, and the if block scales better when each branch grows. Choose the form that makes the next change easiest to make.
Testing with boundary values
Every choice hides a boundary. The condition total >= 50 changes its answer between 49 and 50, and a test that uses 32 never shows that change. For each conditional expression, pick three values: one just below the boundary, the boundary itself, and one just above it. Then trace each one by hand before you run the program.
For the delivery line, the three values are 49, 50 and 51. For 49, the test is False and the charge line is chosen. For 50, the test is True and the free line is chosen. For 51, the test is True and the free line is chosen again. If the code used > instead of >=, the value 50 would print the charge line, and the boundary test would show the mistake at once.
This habit matters more for short expressions than for if blocks, because a one-line expression is easy to skim. A reader sees a short line and assumes it is correct. Writing the three values next to the expression forces you to read the operator, and the operator is where the bugs hide.
For a two-way choice with a range, such as a value that must be from 1 to 10, test the values 0, 1, 10 and 11. Each boundary is a place where < instead of <= gives a different answer, so each boundary belongs in the list.
Names that make a condition read well
A conditional expression is easier to read when its condition has a good name. A variable with a clear name turns a confusing test into a sentence. Compare these two lines:
label = "Free delivery" if total >= 50 and not has_coupon else "Delivery charge"qualifies_for_free_delivery = total >= 50 and not has_coupon
label = "Free delivery" if qualifies_for_free_delivery else "Delivery charge"The second version costs one extra line, and it tells the reader what the test means. When you come back to the code in a month, the name explains the rule without a comment. The name also gives you a place to test the rule on its own, by printing qualifies_for_free_delivery before the choice.
Good names are short and say what is true. Names such as is_weekend, is_member or has_ticket read naturally inside a conditional expression. Names such as flag, x or b force the reader to go back and check what the test means, and that slows every later change. Python's style guide, PEP 8, asks for lower-case names with underscores for variables and functions, so is_member is the form you will see in Python code.
max and min: which to use
You already wrote a if a > b else b to find the larger of two values. Python has a built-in function, max(a, b), that does the same job, and min(a, b) for the smaller value. For a beginner, the conditional expression shows exactly how the choice is made, which helps learning. In everyday Python code, max and min are usually better, because the name says what the code means and the reader does not have to decode the test.
9 4Use the conditional expression when the choice is not a plain maximum or minimum. For example, the choice between "Free delivery" and a price text is not a number comparison, so no built-in function fits. In an interview, write the conditional expression first to show the logic, then mention the built-in function.
Reading it aloud
A useful trick is to read a conditional expression aloud in plain words. So "Free delivery" if total >= 50 else "Delivery charge" reads as "Free delivery if the total is at least 50, otherwise Delivery charge". If you cannot read an expression in one sentence like this, it is probably too complicated for one line.
Chains are where this trick shows its value. Take the sign label from earlier: "positive" if x > 0 else "negative" if x < 0 else "zero". Read it aloud. "Positive if x is greater than 0, otherwise negative if x is less than 0, otherwise zero." The sentence works, which means the expression is correct. Now read a version with a third level and a fourth. The sentences get long, and that is the signal to switch to an if and elif ladder.
Reading aloud also helps you catch a missing else. If you read an expression and cannot say the "otherwise" part, the expression is incomplete. Python will refuse to run it, as the SyntaxError above showed, but reading aloud shows you the gap before you type the rest of the program.
When a choice sits inside a larger expression
Conditional expressions sit well inside calls and string joins. A common pattern is a message that depends on a count, placed inside a print with other values. An f-string works best, because the braces keep the parts apart.
You have 2 messagesWithout an f-string, the same line needs brackets around the choice and a str conversion for the number:
You have 2 messagesThe brackets keep the choice as one unit, so the words are joined in the right order. Use brackets whenever a conditional expression sits inside a larger expression.
What a conditional expression costs
A conditional expression costs about the same as the equivalent if and else block. Both run one test and then one branch. Python's own timing depends on the machine and the version, so this lesson does not give numbers. The choice between them is about how the code reads and how easily it changes.
For a rough size of effort, a conditional expression is one test, and the time to run it is constant, O(1). A chain of three choices is three tests, still constant. None of these grows with the size of the input. If a program is slow, look at loops and repeated work, not at the choice of expression or block.
Following a chain step by step
Take the sign label again: "positive" if x > 0 else "negative" if x < 0 else "zero". Trace three values, because each one stops at a different point in the chain.
For x = 7, the first test x > 0 is True. The answer is "positive", and the rest of the chain is never read at all. For x = -2, the first test is False, so Python moves to the value after the first else, which is the second conditional expression. Its test x < 0 is True, so the answer is "negative". For x = 0, the first test is False, and the second test x < 0 is also False, so the answer is the last value, "zero".
The value 0 is the one that needs both tests to fail. It is the boundary of the middle case, and it is the value most likely to be left out of a test set. When you write a chain, make sure at least one input reaches the final value, and at least one reaches each earlier value. A chain with a missing path is a bug that appears only for the inputs nobody tried.
Each part of the chain is evaluated only when the previous test chose its else. The same rule for a single conditional expression applies to the whole chain. Python never computes a value it does not need, so a branch that divides by zero or adds to None is safe as long as the earlier test rules it out.
Frequently asked questions
Can I leave out the else part? No. A conditional expression always has two values, because it must produce one. If you need an action on only one side, write an if block with no else.
Does a conditional expression evaluate both branches? No. Only the chosen branch runs, so a function call in the other branch is not called. A line with two function calls, such as describe(True) if ok else describe(False), calls exactly one of them.
Can one conditional expression sit inside another's condition? You can, but it is hard to read. If a condition needs its own choice, compute that choice first and give it a name. The name makes the rest of the line shorter and the intent clearer.
Can the result be None? Yes, if one branch gives None. Storing and printing None is safe. Adding to it or calling a method on it fails at that point, as the earlier TypeError showed.
Is the conditional expression faster than an if block? Not in any way that matters for a lesson or a small program. Both run one test and one branch. Choose the form that reads best for the next person.
Choosing the right tool
A short summary helps when you are not sure which form to use. The table below covers the common cases.
| Situation | Best choice |
|---|---|
| Pick one of two values and use it on the same line | Conditional expression |
| Run actions in each branch, such as printing or saving | if and else block |
| Three or more choices, each with a condition | if and elif ladder, or match for exact values |
| The bigger or smaller of two numbers | max or min |
| A choice that depends on a named variable | Conditional expression, with a name for the variable |
Common mistakes and the messages Python gives you
Mistake 1: a conditional expression with no else part. You write kind = "even" if n % 2 == 0 and Python reports SyntaxError: expected 'else' after 'if' expression. A conditional expression always has both values. If you need a one-sided choice, write an if block.
Mistake 2: a conditional expression used for an action. You write print("left") if flag else print("right"). It runs, but it is hard to read. Use an if block, as explained above.
Mistake 3: joining text and a number without a conversion. You write "You have " + count + " message" and Python reports TypeError: can only concatenate str (not "int") to str. Use an f-string, or str(count).
Mistake 4: mixing types without noticing. Python accepts s = "x" if flag else 5, and the error appears later, on the line that uses s, as shown above. Check the type of both branches before you use the value.
Mistake 5: nesting too deeply. Three levels of conditional expressions are very hard to read. Switch to an if and elif ladder when you pass two nested levels.
Mistake 6: using a None value. As shown above, None + 1 fails with TypeError: unsupported operand type(s) for +: 'NoneType' and 'int' at the moment the branch is chosen.
Debugging walkthrough: the message that breaks
A student writes the message code below and gets an error. They expect You have 1 message.
count = 1
print("You have " + count + " message" if count == 1 else " messages")TypeError: can only concatenate str (not "int") to strStep 1: write down the values. count is 1, and it is an integer.
Step 2: Python tests the condition first. count == 1 is True, so the first value is chosen.
Step 3: the first value is "You have " + count + " message". The conditional expression has the lowest precedence of these operators, so the whole text join is the first value. Python joins the text from left to right. The first + joins "You have " and count, which is an integer.
Step 4: a string and an integer cannot be joined with +, so Python stops with the TypeError.
The fix is to convert the number and put brackets around the choice:
You have 1 messageThe brackets make the choice a single value, and str(count) makes the join work. Lesson: when a conditional expression sits inside a larger expression, put brackets around it. The cost is two characters, and the error it prevents is easy to miss.
Interview questions about the conditional expression
Interviewers like this form because it tests whether you can write a choice compactly without making it unreadable. Three questions come up often.
The first is the absolute value without a library call. The answer is x if x >= 0 else -x. Python has the built-in abs, so mention it after the logic. Python integers have no fixed size, so the largest or smallest value cannot overflow here.
The second is the maximum or minimum of two values. The answer is a if a > b else b for the maximum and a if a < b else b for the minimum. Explain that equal values give the second branch, which is fine because they are equal. Then mention that max and min do the same work.
The third is clamping, shown above. Ask whether the range can be empty, meaning low greater than high, and state what your code returns in that case. The two versions in this lesson return different values for an empty range, which is a good point to make in an interview. A good answer names the invalid input and protects it with a check at the top of the function.
Each of these computes a value with a fixed number of tests. Time complexity is O(1), and space complexity is O(1). None of them does work that grows with the input.
Real-world uses
Conditional expressions appear in ordinary application code often. A user interface label reads Save or Saving... depending on a flag. A price shows Free when it is 0 and the number otherwise. A message uses item or items. An API response chooses a default value when a field is missing. In each of these, the choice produces one value, and that value is used right away.
When you read real code, check that the expression has no side effects, that both branches give values you can use the same way, and that None cannot be chosen when the next line needs a real value. Those three checks cover almost every bug of this kind.
Practice
Each exercise has an answer below a "Show the answer" line. Try each one before you read the answer. When the exercise gives sample inputs, type them when the program asks for input.
- Use a conditional expression to store
"pass"or"fail"for a mark, with 40 as the pass mark. Print the result for40,39and100.
Show the answer
The boundary 40 is a pass, because the test is >=. The results are pass, fail and pass. The code is result = "pass" if mark >= 40 else "fail" followed by print(result).
- Print the absolute value of a number without the built-in
abs. Test it with-7,0and12.
Show the answer
Use value = n if n >= 0 else -n, then print value. The outputs are 7, 0 and 12. The built-in abs gives the same answers.
- Rewrite the three-way sign example as an
ifandelifladder with three branches, and explain why the ladder is easier to read.
Show the answer
The ladder is if x > 0: with sign = "positive", then elif x < 0: with sign = "negative", then else: with sign = "zero". Each test has its own line, and the three outcomes are clear. A reader does not need to count brackets to know which branch applies.
- Write a conditional expression that gives
evenoroddfor the numbers 0, 1, 2, -1 and -2 in turn. Predict the output for each before you run it, and check the negative numbers.
Show the answer
The outputs are even, odd, even, odd and even. In Python, -1 % 2 is 1, so the == 0 test picks odd correctly for the negative numbers.
- A student wrote
print(count + " " + "item" if count == 1 else "items")and got an error forcount = 1. Explain the error and write a fixed line.
Show the answer
The condition is True, so the first value, count + " " + "item", is chosen. Adding an integer to a string raises TypeError: unsupported operand type(s) for +: 'int' and 'str'. The fixed line is print(f"{count} {'item' if count == 1 else 'items'}"). The f-string prints the count, and the conditional expression chooses the word.
- Explain why
r = (a := a + 1) if flag else (b := b + 1)is risky, and rewrite it with anifblock that is clear about what changes.
Show the answer
Only one of the two variables changes, and a reader must notice which one from the condition. The if block is:
if flag:
a = a + 1
r = a
else:
b = b + 1
r = bThe effect is the same, but each branch is visible, and the reader knows which variable moves.
- Write a conditional expression that returns the larger of two numbers, and check what it gives for
2and2.5. Explain the result.
Show the answer
The expression is a if a > b else b. For 2 and 2.5, the test 2 > 2.5 is False, so the result is 2.5. Python does not widen the result in this case, so the value is 2.5, which is the float. If the test had chosen 2, the result would be the integer 2, with no .0 added.
- Explain why
value = maybe + 1 if use_default is False else 0fails whenmaybeisNone, and say what changes whenuse_defaultisTrue.
Show the answer
When use_default is False, the test is True and the first branch, maybe + 1, is chosen. Adding 1 to None raises a TypeError. When use_default is True, the second branch, 0, is chosen, so maybe + 1 never runs and the program is safe.
- Write a conditional expression that gives the shipping cost:
0when the order is at least 50, and5otherwise. Then write the same choice as a function that takes the total and returns the cost. Check the total 50, which should be free shipping.
Show the answer
The expression is cost = 0 if total >= 50 else 5. The function is:
def shipping(total):
return 0 if total >= 50 else 5The total 50 gives 0, because the test is >=.
- Explain why a conditional expression that only prints is a poor choice, and say which form you would use instead.
Show the answer
A conditional expression chooses a value, and printing is an action. Python runs the line, but the expression's value (None, the result of print) is thrown away, and a reader must work out what it does. Use an if block with a print in each branch. The conditional expression is for choosing a value that you then use.
- Clamp a value to the range 1 to 100 with a conditional expression. Test the values 0, 50 and 150.
Show the answer
The code is v = 1 if value < 1 else (100 if value > 100 else value), then print(v). The outputs are 1, 50 and 100. The first test catches values below the range, and the nested test catches values above it.
- Write a conditional expression that gives the word
Emptywhen a string has no characters, and the string itself otherwise. Test it with""and"hi". Then explain what happens when the string isNone, and how to guard against it.
Show the answer
The code is shown = "Empty" if len(text) == 0 else text. The outputs are Empty and hi. When text is None, len(text) raises a TypeError. Check for None first, with "Empty" if text is None or len(text) == 0 else text, so the length is never read from None.
What you now know
- A conditional expression,
value_if_true if condition else value_if_false, produces one value, and it fits inside a print, a return or an assignment. - Put brackets around a conditional expression inside a larger expression, and use an f-string when you join text and numbers.
- Python does not widen the two branches. The chosen branch decides the type, and a mismatch can appear later, on the line that uses the value.
- Only the chosen branch is evaluated, so a
Noneor a division by zero in the other branch does no harm. - Use an
ifblock for actions, and anifandelifladder for three or more choices. - Use
maxandminfor the bigger or smaller of two numbers.