HSHimanshu Sharma
Python Notes

Python operators: arithmetic, comparison, logical, bitwise, assignment and more

Python operators: arithmetic, comparison, logical, bitwise, assignment and more Advertisement

Operators are the symbols that combine values: +, ==, and, in. Almost every line of Python uses them, and a few have surprising behaviour: floor division with negative numbers, and returning a value rather than True, is versus ==. This post goes through them group by group with output from real runs.

Arithmetic operators

print(7 + 2, 7 - 2, 7 * 2, 7 / 2, 7 // 2, 7 % 2, 2 ** 3)
# 9 5 14 3.5 3 1 8
Operator Meaning Note
+ - * add, subtract, multiply + and * also join and repeat strings and lists
/ true division always returns a float
// floor division rounds down toward negative infinity
% remainder (modulo) the sign follows the divisor
** power right to left: 2 ** 3 ** 2 is 2 ** 9

Negative numbers show the difference between truncating and flooring:

print(-7 // 2, -7 % 3, 7 % -3, divmod(-7, 3))      # -4 2 -2 (-3, 2)
print(2 ** -1, 10 ** 20, 0.1 * 3)                    # 0.5 100000000000000000000 0.30000000000000004

a == (a // b) * b + a % b always holds, which is why -7 // 2 is -4 and not -3. Integers have no size limit (10 ** 20 is exact), while floats are approximate. divmod(a, b) returns the quotient and remainder together. Division by zero raises ZeroDivisionError.

Comparison (relational) operators

<, <=, >, >=, ==, != return True or False.

print(3 > 2, 3 < 2, 3 >= 3, 3 <= 2, 3 == 3.0, 3 != 4, "a" == "a", "10" == 10)
# True False True False True True True False

3 == 3.0 is true (numbers compare by value), while "10" == 10 is false: a string is never equal to a number. Strings compare by character code, lists and tuples compare item by item.

Python allows chained comparisons, which read like mathematics:

print(1 < 2 < 3, 1 < 3 < 2)        # True False

1 < x < 10 is clearer and safer than 1 < x and x < 10.

Equality versus identity

== asks "are the values equal?" is asks "are they the same object?"

a = [1, 2]; b = [1, 2]; c = a
print(a == b, a is b, a is c, a is not b)       # True False True True

Use is only for None (if x is None) and for sentinel objects. For numbers and strings, is can seem to work (small integers are shared, 256 is 256) and then fail for no obvious reason, so use ==.

Logical operators

and, or, not. They work on truthiness, and and / or return one of their operands, not necessarily True or False.

print(True and False, True or False, not True)       # False True False
print(0 or "x", "" or "y", 0 and 5, 5 and 6, None or [])
# x y 0 6 []

The rule: a or b returns a if a is truthy, else b. a and b returns a if a is falsy, else b. Evaluation short-circuits, so the right side runs only if needed:

print((0 and 1) or 2, 1 and 2 or 3)       # 2 2

This allows idioms such as name = user_input or "Guest", and guards such as if items and items[0] > 5: (the index is never evaluated for an empty list). Be careful with value or default when 0 or "" are valid values, since they are falsy. not always returns a real boolean.

Bitwise operators

Bitwise operators work on the binary digits of integers.

print(5 & 3, 5 | 3, 5 ^ 3, ~5, 5 << 2, 5 >> 1, -5 >> 1)
# 1 7 6 -6 20 2 -3
print(bin(5 & 3), bin(5 | 3), bin(5 ^ 3))         # 0b1 0b111 0b110
Operator Name Example (5 = 101, 3 = 011)
& AND (1 where both are 1) 101 & 011 = 001 → 1
| OR (1 where either is 1) 101 | 011 = 111 → 7
^ XOR (1 where they differ) 101 ^ 011 = 110 → 6
~ NOT (flip all bits) ~x == -x - 1
<< shift left x << n is x * 2**n
>> shift right x >> n is x // 2**n

~5 is -6 because Python integers are signed with unlimited width: ~x equals -(x + 1). To see a fixed width, mask: format(~5 & 0xFF, "08b") gives 11111010. Practical uses: flags and permissions, checking odd or even (n & 1), fast multiplication or division by powers of 2, and low-level protocols.

n = 15
print(n & 1 == 1, "odd" if n & 1 else "even")      # True odd
print(1 << 10, 0b1111 & 0b0101)                      # 1024 5

Assignment operators

= binds a name; the augmented forms combine an operation with assignment: +=, -=, *=, /=, //=, %=, **=, &=, |=, ^=, <<=, >>=.

x = 5; x += 2; x -= 1; x *= 3; x //= 4; x **= 2; x %= 5
print(x)           # 1   (5 -> 7 -> 6 -> 18 -> 4 -> 16 -> 1)
y = 7; y <<= 1; y >>= 2; y &= 2; y |= 4; y ^= 1
print(y)           # 7

There is no ++ or -- in Python; write x += 1. Multiple assignment and swapping use tuple unpacking: a, b = b, a.

For mutable objects, += changes the object in place (lst += [3] extends the list), while lst = lst + [3] creates a new one. This difference matters when two names share the list.

Ternary (conditional) operator

print("yes" if 5 > 3 else "no")                         # yes
print((lambda a, b: a if a > b else b)(3, 9))           # 9

The form is value_if_true if condition else value_if_false. Do not nest it more than once; use if statements instead.

Identity and membership operators

  • is, is not: same object or not (above).
  • in, not in: membership in a sequence, set or dictionary (keys).
print("a" in "cat", 3 not in [1, 2], "k" in {"k": 1}, 1 in {1}, 2 in (1, 2))
# True True True True True

For strings, in tests for a substring. For dictionaries it tests keys. For large collections, sets and dictionaries test membership much faster than lists (see sets).

Operator precedence

When an expression mixes operators, Python follows a fixed order. From highest to lowest (roughly):

  1. **
  2. unary +x, -x, ~x
  3. *, /, //, %
  4. +, -
  5. <<, >>
  6. &, then ^, then |
  7. comparisons, is, in (all the same level, and chainable)
  8. not
  9. and
  10. or
  11. conditional if else
  12. assignment
print(1 + 2 * 3, (1 + 2) * 3, 2 ** 3 ** 2, -2 ** 2, not 1 == 2, 10 - 4 - 3, 100 / 10 / 5)
# 7 9 512 -4 True 3 2.0

Note -2 ** 2 is -4 (the power binds tighter than the minus) and 10 - 4 - 3 is 3 (left to right). When in doubt, use brackets: they cost nothing and make the intent clear.

The math module

Operators cover basic arithmetic. For more, import math:

import math
print(math.sqrt(25), math.ceil(4.1), math.floor(-4.1), math.trunc(-4.9), math.pi)
# 5.0 5 -5 -4 3.141592653589793
print(math.log(math.e), math.log10(1000), math.log2(8), math.hypot(3, 4), math.degrees(math.pi))
# 1.0 3.0 3.0 5.0 180.0
print(math.gcd(12, 18), math.factorial(5), math.isclose(0.1 + 0.2, 0.3), math.isnan(float("nan")))
# 6 120 True True

Two reminders. round() uses banker's rounding (half goes to the even number): round(4.5) is 4 and round(5.5) is 6. And comparing floats needs care:

print(0.1 + 0.2 == 0.3, round(0.1 + 0.2, 2) == 0.3, math.isclose(0.1 + 0.2, 0.3))
# False True True

Common mistakes

  • Using = where == is meant (Python reports a syntax error in conditions).
  • Expecting // to truncate negative numbers toward zero.
  • Using is to compare numbers or strings.
  • Expecting and / or to return True / False.
  • Writing x++.
  • Forgetting that ^ is XOR, not power (**).
  • Comparing floats exactly.
  • Not parenthesising mixed and / or expressions.

Practice

Write a function that tells whether a year is a leap year using only %, and, or (a year is a leap year if divisible by 4, except century years unless divisible by 400). Then use & to test whether a permission value has the "write" bit (value 2) set. Finally, predict the output of print(2 + 3 * 4 ** 2 // 5 % 3) before running it.

Continue

These operators act on the types in the fundamentals post; next, see how strings and tuples use them. For a quick percentage calculation outside code, try the Percentage Calculator, and see the Python cheat sheet for a summary.

Advertisement