Back to Blog
Python

Python and vs &: Logical and Bitwise Operators

python and vs &: Explains the difference between Python's `and` and `&` operators, covering precedence, short-circuiting, and common mistakes.

Python operatorslogical operatorsbitwise operatorsoperator precedenceboolean logiccommon pitfalls
Illustration contrasting Python's logical and operator with the bitwise and operator, showing two distinct paths merging into a single result.

python and vs & requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you write a and b in Python, you are using the logical AND operator. When you write a & b, you are using the bitwise AND operator. The two behave very differently, and confusing them leads to subtle bugs that are hard to trace. This article explains the core differences, how evaluation order affects your code, and where each operator belongs.

The Core Difference Between and and &

The logical and evaluates operands in a boolean context. It returns the first falsy operand if any exists, otherwise it returns the last operand. This means and does not always return True or False; it returns one of the original values. For example:

result = 0 and 10 # 0, because 0 is falsy result = 2 and 3 # 3, because both are truthy and 3 is last

The bitwise & operates on the binary representations of integers. It compares each bit position and returns an integer where a bit is set only if both corresponding bits are set. For example:

result = 5 & 3 # 1, because 5 (101) & 3 (011) = 001

If you apply & to booleans, Python treats True as 1 and False as 0, but the operation is still bitwise and returns an integer value, not a boolean. This is a common source of confusion.

Operator Precedence and Evaluation Order

& has higher precedence than and. In an expression without parentheses, Python evaluates & before and. Consider:

x = 1 or 2 & 3 # 1, because 2 & 3 = 2, then 1 or 2 = 1

If you mix both operators, you must use parentheses to make the intent clear. Relying on precedence alone is fragile because the reader may not immediately know that & binds tighter. For example, a and b & c is parsed as a and (b & c), which is probably not what you intended if you wanted (a and b) & c.

Precedence also affects expressions like x & y == z. In Python, == has higher precedence than &, so x & y == z is parsed as x & (y == z). This is a classic bug. Always parenthesize bitwise and logical operations when mixing them with comparisons.

Short-Circuiting Behavior

The logical and short-circuits. If the left operand is falsy, Python does not evaluate the right operand at all. This is important for performance and for avoiding errors when the right operand has side effects or depends on the left operand:

def divide(a, b): return b != 0 and a / b # avoids ZeroDivisionError

The bitwise & does not short-circuit. Both operands are fully evaluated before the bitwise operation runs. If you use & in a condition where you expect short-circuiting, you may trigger unnecessary work or exceptions. For example, x != 0 & 10 / x > 1 will evaluate 10 / x even if x is zero, raising an error.

Using & on Booleans

You can apply & to boolean values, but the result is not guaranteed to be a boolean. In Python, True & False returns False because 1 & 0 is 0, and 0 is falsy. However, True & True returns True (which is 1). The result is an integer, not a boolean object. This matters when you rely on the exact type of the result:

type(True & False) # <class 'int'>

If you need a boolean result, use and or convert explicitly with bool(). Using & for logical conditions works by accident in many cases, but it fails when you need short-circuiting or when you rely on the return type.

Common Mistakes and Pitfalls

The most frequent mistake is using & where and is intended, especially when porting code from languages like C or JavaScript where && is the logical operator. In Python, & is not a synonym for and. Consider a typical condition:

if is_admin & user_active: # This works if both are booleans, but it does not short-circuit # and it returns an int, not a bool

This works for simple boolean variables, but if user_active is a property or a function call, it is always evaluated, even when is_admin is False. That can cause performance problems or unexpected side effects.

Another pitfall is using & with lists or other iterables. Python does not support bitwise AND on lists; you get a TypeError. If you see code like list1 & list2, the developer likely meant set intersection or a logical AND over elements, but neither works with & directly.

Readability and Maintainability Considerations

Using and for logical conditions makes the intent explicit. The reader immediately knows that the expression is a boolean test. Using & for bitwise manipulation is appropriate when you are working with flags, masks, or binary protocols. Mixing them without clear separation makes code hard to read and maintain.

A practical guideline is to reserve & for operations where the operands are integers and the result is an integer. If you are working with booleans and want a logical AND, always use and. This keeps the code predictable and avoids surprising behavior when operands change type later.

When to Use Each Operator

Use and when you need logical conjunction, short-circuiting, or when the operands are arbitrary objects and you want to return one of them. Use & when you are performing bitwise operations on integers, such as extracting flags, masking values, or implementing binary algorithms.

For example, to check if a number is even, number & 1 == 0 is a common bitwise trick, but number % 2 == 0 is more readable. Use & only when the bitwise nature is essential to the algorithm, such as when working with hardware registers or network protocols.

If you find yourself writing a & b where both a and b are booleans, stop and reconsider. The logical and is almost always the correct choice because it short-circuits and returns a boolean. The only exception is when you deliberately need to avoid short-circuiting, but that is rare and usually a code smell.

Precedence Traps with Comparisons

A particularly sneaky bug arises when you combine & with comparison operators. Because == and != have higher precedence than &, an expression like x & y == z is parsed as x & (y == z). This is almost never what you want. For example:

if flags & 0x01 == 0x01: # This is actually flags & (0x01 == 0x01) = flags & True = flags & 1

To check whether a specific bit is set, you must parenthesize the bitwise operation:

if (flags & 0x01) == 0x01: # Correct

Always wrap bitwise operations in parentheses when they appear alongside comparisons or logical operators. This is a simple rule that prevents a whole class of precedence-related bugs.

Final Code Example: Correct Mixing

Here is a realistic example that uses both operators correctly:

class Permission: READ = 0x01 WRITE = 0x02 EXECUTE = 0x04 permission = Permission.READ | Permission.WRITE # bitwise OR if permission & Permission.READ and permission & Permission.WRITE: print("Can read and write")

The & extracts individual bits, while and combines the two boolean checks. The parentheses around each & expression are necessary because and has lower precedence. Without them, the expression would be parsed as permission & (Permission.READ and permission) & Permission.WRITE, which is wrong.

Understanding python and vs & is not just about memorizing syntax; it is about knowing which operator matches the data type and evaluation semantics you need. When in doubt, prefer and for logic and & for bitwise work, and always parenthesize mixed expressions.

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