Back to Blog
Python

Python or vs |: Logical vs Bitwise

python or vs |: Understand the difference between Python's `or` logical operator and the `|` bitwise operator, including type unions and common pitfalls.

Pythonlogical operatorsbitwise operatorstype hintsPython syntax
Diagram showing Python's or operator returning a truthy value while the bitwise pipe operator combines binary bits.

When developers search for python or vs |, they usually hit a common point of confusion: the or keyword and the | operator look similar but behave very differently. In Python, or is a logical operator that returns the first truthy operand, while | is a bitwise OR that works on integers, sets, and, since Python 3.10, type hints. Choosing the wrong one can lead to subtle bugs that are hard to trace.

The Core Difference: Logical vs Bitwise Semantics

The fundamental distinction lies in what each operator evaluates. or performs logical disjunction on truthy values, short-circuiting as soon as it finds a truthy operand. | performs a bitwise OR on the binary representations of integers, or a union operation on sets, and does not short-circuit. For example:

# Logical or: returns the first truthy value result = 0 or 42 # 42 result = "" or "default" # "default" result = None or [] # [] # Bitwise or: operates on integer bits result = 5 | 3 # 7 (binary 101 | 011 = 111) result = 0 | 0 # 0

The or operator does not always return a boolean; it returns one of the operands based on truthiness. This makes it useful for default values and fallbacks. The | operator, when applied to integers, returns an integer with bits set in either operand. It has no concept of truthiness and always evaluates both sides.

How or Behaves: Short-Circuiting and Truthiness

or evaluates the left operand first. If it is truthy, Python returns it without evaluating the right operand. This short-circuit behavior is both a performance optimization and a way to avoid errors when the right side depends on the left side being falsy. For instance:

def get_config(): return {"timeout": 30} config = get_config() or {}

If get_config() returns a non-empty dictionary, it is used directly; otherwise, an empty dictionary is the fallback. This pattern is idiomatic and often clearer than an explicit if statement.

Because or returns the operand itself, the result type can vary. This is useful but can also surprise developers who expect a boolean. For example, 1 or 2 returns 1, not True. If you need a strict boolean, you must explicitly convert: bool(1 or 2).

How | Behaves: Bitwise Operations and Set Union

For integers, | performs a bitwise OR, setting each bit to 1 if either operand has that bit set. This is common in low-level programming, flags, and permission systems. For example:

READ = 1 # 001 WRITE = 2 # 010 EXECUTE = 4 # 100 permissions = READ | WRITE # 3 (011)

| also works on sets, returning a new set with elements from both operands:

a = {1, 2, 3} b = {3, 4, 5} union = a | b # {1, 2, 3, 4, 5}

Unlike or, | does not short-circuit; both operands are always evaluated. This matters when the right side has side effects or is expensive to compute.

Using | for Type Unions in Modern Python

Since Python 3.10, | can be used in type hints to create a union type. For example, int | str means the value can be either an int or a str. This syntax is more concise than Union[int, str] from the typing module:

def parse(value: int | str) -> None: ...

This usage is purely for type annotations and does not affect runtime behavior. It is a separate context from bitwise operations, but it can confuse developers who see | used in both places. The | operator in type hints is not evaluated at runtime; it is only used by static type checkers and IDEs.

Common Mistakes and How to Avoid Them

One frequent error is using | instead of or in a logical condition. For example:

# Wrong: bitwise OR on integers, not logical if x | y: ... # Correct: logical OR if x or y: ...

If x and y are integers, x | y performs a bitwise operation and returns an integer, which may be truthy even when both operands are falsy. For instance, 0 | 0 is 0 (falsy), but 1 | 2 is 3 (truthy), which might not be the intended logic.

Another mistake is assuming or returns a boolean. In a condition, Python implicitly converts the result to a boolean, but if you store the result, it may be a non-boolean value. This is not a bug per se, but it can lead to unexpected behavior if you later compare with == True.

Performance and Readability Considerations

or short-circuits, which can save work when the right operand is expensive. For example, value or compute_default() avoids calling compute_default() if value is truthy. | always evaluates both sides, so it is less efficient when the right side is costly and the left side is already truthy. However, for simple integer or set operations, the performance difference is negligible.

Readability is a more important factor. or clearly signals logical fallback or conditional selection. | signals bit manipulation or set union. Using | for logical conditions makes the code harder to understand and maintain. Similarly, using or for bitwise operations would produce incorrect results. Stick to the intended use of each operator.

Choosing the Right Operator for Your Use Case

The choice depends on what you need to express. Use or when you want to select the first truthy value or combine boolean conditions. Use | when you need to combine integer bit flags, merge sets, or annotate a union type. The following table summarizes the key differences:

OperatorPurposeReturnsShort-circuitsCommon Use Cases
orLogical ORFirst truthy operand or last operandYesDefault values, fallbacks, boolean conditions
`` (integers)Bitwise ORInteger with combined bitsNo
`` (sets)Set unionNew set with elements from bothNo
`` (type hints)Type unionType annotation (no runtime effect)N/A

In practice, if you are writing a condition, use or. If you are manipulating bits or sets, use |. If you are annotating a parameter that can be one of several types, use | in the type hint. Mixing these contexts is the source of most confusion.

Compatibility and Version Requirements

The type union syntax with | was introduced in Python 3.10. If your code must run on Python 3.9 or earlier, you need to use Union from typing instead. This is a critical consideration for libraries that support multiple Python versions. The bitwise | operator for integers and sets has existed since early Python versions, so it has no compatibility concerns.

When using | for type hints, be aware that it is not supported in older Python versions. You can use from __future__ import annotations in Python 3.7+ to defer evaluation of annotations, but the syntax itself is only parsed in Python 3.10+. If you maintain a package with a wide compatibility range, stick to Union or use a conditional import.

For logical or, there are no version-specific issues. It behaves consistently across all Python versions. Understanding these differences helps you write code that is both correct and maintainable, avoiding the pitfalls that often arise when python or vs | is misunderstood.

python or vs |: Practical Usage and Code Examples | RYUSLOG DEV