Real-world decisions rarely come down to a single condition. "Can this person drive?" depends on both their age and whether they have a license. "Is the store open?" might depend on it being a weekday or a holiday. Python logical operators are how you express exactly that kind of multi-condition logic in code.
What Are Logical Operators?
Logical operators combine or invert Boolean expressions to produce a single True/False result. Python has three of them:
and
or
notIf you've used other programming languages, you might expect symbols like && and || here. Python deliberately spells these out as plain English words instead — one more example of the language favoring readability over terse symbols.
These operators are what let you write if statements and loop conditions that check more than one thing at once, which is close to unavoidable in any real program.
The and Operator
and returns True only if both operands are True. If either one is False, the whole expression is False.
Truth table
A | B | A and B |
|---|---|---|
True | True | True |
True | False | False |
False | True | False |
False | False | False |
Practical example
age = 20
has_license = True
can_drive = age >= 18 and has_license
print(can_drive) # TrueBoth conditions have to hold for can_drive to be True. If either one fails — say, has_license is False — the whole expression becomes False, regardless of the person's age.
age = 20
has_license = False
can_drive = age >= 18 and has_license
print(can_drive) # False — age is fine, but no licenseThe or Operator
or returns True if at least one operand is True. It only returns False when both operands are False.
Truth table
A | B | A or B |
|---|---|---|
True | True | True |
True | False | True |
False | True | True |
False | False | False |
Practical example
is_weekend = False
is_holiday = True
store_closed = is_weekend or is_holiday
print(store_closed) # True — at least one condition is metThis pattern is useful for combining conditions python developers often need to check together when any one of several situations qualifies — special pricing that applies on a birthday or during a promotional event, access granted to either an admin or the resource's owner, and so on.
The not Operator
not simply inverts a Boolean value — True becomes False, and vice versa.
print(not True) # False
print(not False) # TrueMaking conditions more readable
not is especially useful for writing conditions that read naturally in plain English, rather than as an awkward equality check:
is_logged_in = False
# Awkward
if is_logged_in == False:
print("Please log in")
# Cleaner — this is the idiomatic Python style
if not is_logged_in:
print("Please log in")The second version is how experienced Python developers write this check. Comparing a Boolean to False with == works, but it's considered unpythonic — not is_logged_in says exactly the same thing more directly.
Combining Operators: Precedence and Short-Circuit Evaluation
Evaluation order
When you combine multiple logical operators in one expression, Python evaluates them in a specific order: not first, then and, then or.
result = not False and True or FalseWorking through it:
not False→TrueTrue and True→TrueTrue or False→True
print(result) # TrueAs expressions get more complex, this precedence order becomes harder to track mentally — which is exactly when parentheses earn their keep:
# Explicit grouping — removes any ambiguity about intent
result = (not False) and (True or False)Both versions above evaluate identically, but the parenthesized one is unambiguous to read at a glance. When in doubt, add the parentheses — it costs nothing and saves the next reader (often you, later) from having to recompute precedence rules in their head.
Short-circuit evaluation
Here's a genuinely useful behavior worth understanding well: Python doesn't always evaluate both sides of and or or. It stops as soon as the overall result is already determined — this is called short-circuit evaluation.
With
and: if the first operand isFalse, the result is guaranteed to beFalseno matter what the second operand is — so Python never bothers evaluating the second one.With
or: if the first operand isTrue, the result is guaranteed to beTrue— so again, Python skips the second operand entirely.
def expensive_check():
print("This ran")
return True
# Short-circuits — expensive_check() never runs, because False and anything is False
result = False and expensive_check()
print(result) # False, and "This ran" is never printedA practical safety pattern
Short-circuit evaluation isn't just an efficiency detail — it enables a genuinely important safety pattern, especially around checking for None before using a value:
user = None
if user is not None and user.is_admin:
print("Access granted")
else:
print("Access denied")If user is not None evaluates to False, Python never even attempts to evaluate user.is_admin — which is exactly what you want, since accessing .is_admin on None would raise an AttributeError. The order here matters: putting the None check first is what makes this pattern safe. Writing it the other way around — user.is_admin and user is not None — would crash before short-circuiting ever had a chance to help.
Truthy/falsy values with logical operators
One more subtlety worth knowing: Python's logical operators don't always return a strict True or False. When operating on non-Boolean values, and and or actually return one of the original operands — whichever one determined the result — rather than converting it to a plain Boolean.
x = ""
y = "hello"
print(x or y) # 'hello' — x is falsy, so Python evaluates and returns y
print(x and y) # '' — x is falsy, so the result is x itself, without checking yThis is a common idiom for providing a fallback or default value:
name = ""
display_name = name or "Guest"
print(display_name) # Guest — falls back since name is an empty (falsy) stringIt's a compact, very common pattern in real Python code — worth recognizing even if you don't reach for it yourself right away.