The ternary operator.
a if condition else b.
Python's one-line conditional. It's not condition ? a : b like in C or JavaScript — it's a if condition else b, which reads as "a if condition is true, otherwise b." It's an expression, not a statement, which means it produces a value. Use it for simple conditional assignments. Use if/else for anything more complex. This page covers the syntax, the precedence traps, and the patterns where ternary actually improves the code.
Short answer: x = a if condition else b — returns a if condition is true, otherwise b. It's an expression — assignable, passable, usable in comprehensions. Not the same as condition ? a : b. Parenthesize when combined with other operators: (a if b else c) + 1. Use it for simple two-choice values. Use if/else for multiple statements or side effects. Nested ternaries are valid but risky — past two levels, use if/elif/else.
✅ The syntax
Python's order is different from C.
age = 20
# C/JS style (NOT Python)
# result = age >= 18 ? 'adult' : 'minor' # SyntaxError
# Python style
result = 'adult' if age >= 18 else 'minor'
print(result) # 'adult'
# The structure:
# value_if_true if condition else value_if_false
# Simple value selection
x = 10
y = 20
max_val = x if x > y else y
print(max_val) # 20
# In a function return
def sign(n):
return 'positive' if n > 0 else 'negative' if n < 0 else 'zero'
print(sign(5)) # 'positive'
print(sign(-3)) # 'negative'
print(sign(0)) # 'zero'
# In a function argument
print('yes' if age >= 18 else 'no') # 'yes'
# In a list comprehension
nums = [-2, -1, 0, 1, 2]
clamped = [x if x > 0 else 0 for x in nums]
print(clamped) # [0, 0, 0, 1, 2]
# In a dict value
config = {'debug': True if age > 0 else False}
print(config) # {'debug': True}
# Assigned to a variable
status = 'active' if age >= 18 else 'inactive'
print(status) # 'active'
Syntax comparison:
C, JavaScript, Java, C#:
condition ? value_if_true : value_if_false
age >= 18 ? 'adult' : 'minor'
Python:
value_if_true if condition else value_if_false
'adult' if age >= 18 else 'minor'
Why the different order:
The C ternary puts the condition first — you read
"if condition then X else Y" as "condition ? X : Y".
Python puts the value first — you read "X if
condition else Y" as "give me X in the case
where condition holds."
Both are valid. Python's order matches natural
language better: "give me 'adult' if the person
is 18 or over, otherwise 'minor'."
The structure:
┌── value_if_true
│ ┌── condition
│ │ ┌── value_if_false
│ │ │
'adult' if age >= 18 else 'minor'
Read left to right:
"'adult' if age >= 18, else 'minor'."
The condition can be any expression:
x if x else y # truthiness
x if x > 0 else y # comparison
x if name else y # any truthy value
x if a and b else y # boolean operators
x if not flag else y # negation
x if func() else y # function call
The values can be any expression:
'a' + 'b' if cond else 'c' + 'd'
fn1() if cond else fn2()
[1, 2] if cond else [3, 4]
{k: v} if cond else {}
None if cond else value
Ternary is an expression, not a statement.
You can use it anywhere an expression is allowed.
Where it's not allowed:
❌ condition ? a : b # not Python
❌ a if cond else b if cond2 else c # ambiguous
(actually valid, but reads confusingly)
❌ var = a if cond else b = c # can't assign in ternary
The last one:
You can't do `x = a if cond else b` where a or b is
an assignment. Ternary chooses between values,
not between statements.
✅ Ternary vs if/else
Expression vs statement.
age = 20
# ✅ Ternary — for simple value selection
status = 'adult' if age >= 18 else 'minor'
# ✅ if/else — same result, more lines
if age >= 18:
status = 'adult'
else:
status = 'minor'
# Both work. Ternary is shorter.
# ❌ if/else for simple value — verbose
if age >= 18:
result = 'adult'
else:
result = 'minor'
# ✅ ternary — cleaner
result = 'adult' if age >= 18 else 'minor'
# ❌ ternary for multiple statements — impossible
# You can't do:
# do_something() if cond else do_something_else()
# and call another function after
# Each branch of a ternary is a single expression.
# ✅ if/else — for multiple statements
if age >= 18:
status = 'adult'
log('Adult access granted')
notify('Welcome')
else:
status = 'minor'
log('Minor access restricted')
# ❌ ternary for side effects — confusing
# result = log('hi') if cond else log('bye')
# This works technically, but the intent is unclear.
# log returns None, so result is None either way.
# Better to use if/else for side effects.
# ✅ ternary — pure value selection
result = min(a, b) if a < b else b
# This computes a value. Readable.
# ❌ ternary — value selection with side effects
result = (do_x(), x)[0] if cond else (do_y(), y)[0]
# Tuples with side effects. Terrible.
# Use if/else.
# The rule:
# Ternary — I'm choosing between two VALUES.
# if/else — I'm choosing between two CODE PATHS.
# Ternary produces a value:
result = a if cond else b
# if/else runs code:
if cond:
do_a()
do_b()
else:
do_c()
do_d()
The two roles:
Ternary — expression:
→ produces a value
→ one line
→ both branches must be expressions
→ can be used inside other expressions
→ examples:
x = a if cond else b
return 'yes' if ok else 'no'
[x if x > 0 else 0 for x in nums]
f(a if cond else b)
if/else — statement:
→ runs code
→ multi-line
→ each branch can have any statements
→ can't be used as an expression
→ examples:
if cond:
x = a
log(x)
else:
x = b
log(x)
The gray area:
# These are equivalent:
status = 'adult' if age >= 18 else 'minor'
if age >= 18:
status = 'adult'
else:
status = 'minor'
# Both are correct. Use ternary for brevity.
# These are NOT equivalent:
result = a if cond else b
# vs
if cond:
result = a
else:
result = b
# Actually — they ARE equivalent. But the ternary
# is one line, the if/else is four. For a single
# assignment, prefer the ternary.
When ternary hurts readability:
# Nested ternary — hard to parse
grade = 'A' if s >= 90 else 'B' if s >= 80 else 'C'
# Same logic with if/elif/else — clearer
if s >= 90:
grade = 'A'
elif s >= 80:
grade = 'B'
else:
grade = 'C'
The if/elif/else is longer but each condition
is on its own line. Easier to add cases, change
thresholds, debug.
When ternary improves readability:
# Verbose if/else for a single value
if user:
name = user.name
else:
name = 'Anonymous'
# Compact ternary
name = user.name if user else 'Anonymous'
# The ternary makes it clear: 'choose between
# user.name and 'Anonymous' based on user.'
# The if/else makes you read four lines for the
# same information.
The rule:
If the ternary fits on one line and reads
left to right like English, use it.
If it doesn't, use if/else.
For a single conditional assignment, ternary.
For anything more, if/else.
Performance: no difference.
The choice is about clarity, not speed.
✅ The precedence trap
Where ternary binds relative to other operators.
a = 1
b = 2
c = 3
cond = True
# ⚠️ Arithmetic binds tighter than ternary
# result = a if cond else b + c
# Parsed as: a if cond else (b + c)
# = 1 if True else 5 = 1
print(a if cond else b + c) # 1
# If you meant (a if cond else b) + c:
print((a if cond else b) + c) # 4
# ⚠️ Comparison binds tighter than ternary
# result = a < b if cond else c
# Parsed as: (a < b) if cond else c
# = True if True else 3 = True
print(a < b if cond else c) # True
# If you meant a < (b if cond else c):
print(a < (b if cond else c)) # True
# ⚠️ Assignment binds looser — always parenthesize
x = a if cond else b # OK: x = (a if cond else b)
print(x) # 1
# ⚠️ Lambda binds looser than ternary
# So: lambda: a if cond else b
# is: lambda: (a if cond else b)
f = lambda: a if cond else b
print(f()) # 1
# But: lambda: a, b if cond else c
# is confusing. Parenthesize.
# ⚠️ Nested ternary — right-associative
result = 'a' if cond else 'b' if cond else 'c'
# Parsed as: 'a' if cond else ('b' if cond else 'c')
# Since cond is True, result is 'a'
print(result) # 'a'
# If cond were False, it'd evaluate:
# 'b' if False else 'c' → 'c'
# ✅ Safe form — always parenthesize
result = 'a' if cond else ('b' if cond else 'c')
# Same parsing, but explicit.
# ✅ Better — if/elif/else for multiple conditions
if cond:
result = 'a'
elif cond:
result = 'b'
else:
result = 'c'
Operator precedence in Python:
From highest to lowest (relevant subset):
1. () — parentheses (always win)
2. ** — exponentiation
3. * / % // — multiplication
4. + - — addition
5. << >> — bit shifts
6. & — bitwise AND
7. ^ | — bitwise XOR / OR
8. in, not in, is, is not, <, <=, >, >=, !=, ==
9. not — logical NOT
10. and — logical AND
11. or — logical OR
12. if ... else ... — conditional expression
13. lambda — lambda expression
14. =, +=, -=, etc. — assignment (lowest)
What this means for ternary:
Ternary is VERY LOW precedence.
Only lambda and assignment bind looser.
So everything else evaluates first:
a if cond else b + c
→ a if cond else (b + c)
→ because + binds tighter than if/else
a == b if cond else c
→ (a == b) if cond else c
→ because == binds tighter
a if cond else b if cond2 else c
→ a if cond else (b if cond2 else c)
→ because nested ternary is right-associative
The safe rules:
✅ Parenthesize the branches if they contain
other operators:
(a + b) if cond else (c * d)
This is always correct. Clear intent.
✅ Parenthesize nested ternaries:
a if c1 else (b if c2 else d)
Even though it parses correctly without
parentheses, the parentheses signal
"this is a nested decision."
✅ Parenthesize when mixing with comparisons:
(a if cond else b) == c
vs
a if cond else (b == c)
Two completely different meanings.
✅ Parenthesize when using ternary as an operand:
(a if cond else b) + 1
vs
a if cond else (b + 1)
+1 applies to the result, or to b, depending
on parentheses.
The rule: if you have to think about precedence,
parenthesize. The parentheses cost nothing, and
they document intent.
The nested ternary — a special case:
x = 'a' if c1 else 'b' if c2 else 'c' if c3 else 'd'
This parses as:
'a' if c1 else (
'b' if c2 else (
'c' if c3 else 'd'
)
)
Right-associative: each if/else covers
everything to its right.
You'd use this for a chain of conditions:
score 90+ → 'A'
score 80+ → 'B'
score 70+ → 'C'
else → 'F'
grade = 'A' if s >= 90 else 'B' if s >= 80 else 'C' if s >= 70 else 'F'
This is readable ENOUGH at 4 levels.
At 5+, use if/elif/else.
The Python style guide (PEP 8) says:
"Compound statements (multiple statements
on the same line) are generally discouraged."
But single-line ternaries are fine.
The line for me: if it doesn't fit in 79
characters (PEP 8's line length), or if you
have to count if/elses to understand it,
use if/elif/else.
Don't nest ternaries more than two levels deep. A two-level nested ternary like 'A' if s >= 90 else 'B' if s >= 80 else 'F' is readable — the chain of conditions is clear. A four-level nested ternary is not. Each additional level makes the reader work harder to trace which condition maps to which value. The fix is always the same: use if/elif/else. It's more lines but each condition is on its own line. You can add cases, change thresholds, or debug by adding a print in one branch. The ternary saves keystrokes at the cost of clarity — and clarity is what maintenance is made of.
⚠️ Common mistakes
The ternary mistakes that cost real time.
condition ? a : bSyntaxError. Fix: a if condition else b.
elsex = a if cond — SyntaxError. Fix: ternary needs both branches.
x = cond if a else b — misinterpreted. Fix: x = a if cond else b.
a if cond else b + 1 parses as a if cond else (b + 1). Fix: parenthesize.
result = do_x() if cond else do_y() — unclear. Fix: use if/else.
4+ levels — unreadable. Fix: use if/elif/else.
[x if x > 0 else 0 for x in nums] — works, but [x if x > 0 for x in nums] is a filter. Different semantics.
Multiple statements, side effects, complex logic — all wrong for ternary. Fix: if/else.
❓ Frequently asked
What is the ternary operator in Python?
The ternary operator is Python's one-line conditional expression: x if condition else y. It evaluates condition; if true, returns x; if false, returns y. It's an expression, not a statement — it produces a value that can be assigned, passed to a function, or used in a comprehension.
What is the difference between ternary and if/else?
Three differences. (1) Ternary is an expression — it produces a value. if/else is a statement — it executes code. (2) Ternary is one line. if/else is multiple. (3) Ternary evaluates one of two expressions; if/else can run any code. Use ternary when choosing between two values. Use if/else when running different code paths.
Can I nest ternary operators?
Yes, but it's usually a bad idea. Nested ternaries like x = 'a' if c1 else 'b' if c2 else 'c' work — Python evaluates right-to-left. But readability drops fast. Beyond two levels, use if/elif/else statements or a dictionary dispatch.
Why does my ternary produce the wrong value?
Two common causes. (1) Operator precedence: a if b else c + 1 is parsed as a if b else (c + 1) — the ternary binds looser than arithmetic. Parenthesize if needed. (2) Evaluation order: a if b else c evaluates b first, then evaluates only one of a or c. If the unused branch has side effects, they don't happen.
Can I use ternary in a list comprehension?
Yes, and it's common. Example: [x if x > 0 else 0 for x in nums] — replaces negatives with zero. The ternary goes before the 'for' clause in a comprehension. If you want to filter based on a condition, use the 'if' clause after 'for': [x for x in nums if x > 0].
Is the ternary operator faster than if/else?
No, effectively the same. In CPython, both compile to similar bytecode. For a simple expression, the difference is measured in nanoseconds. Don't choose based on performance. Choose based on readability.
🔗 Continue learning
Next in this series:
pathlib, os.path, and the try/except pattern that avoids race conditions