Operators
Master Arithmetic, Bitwise CPU logic, and the critical difference between
Identity (is) and Equality (==).
Operators are special symbols in Python that carry out mathematical, relational, or logical
computations. While arithmetic (+, -) is straightforward,
mastering Python requires understanding exactly how Identity Operators
(is) track physical RAM, and how Bitwise Operators
(&, |) manipulate raw binary inside the CPU registers.
Think of Equality (==) like looking at two identical red
Ferrari cars in a parking lot. Are they equal? Yes. They look exactly the same and have the
same specs. This is a value check.
Think of Identity (is) like looking at the license plates. Are
they the exact same physical vehicle? No. They are two different cars parked in two
different spots. Using car1 is car2 returns False because
their physical coordinates in the parking lot (RAM) are different.
# Scenario 1: Identity vs Equality
list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(list1 == list2) # True
print(list1 is list2) # False
# Scenario 2: Bitwise Shift
x = 10 # Binary: 0000 1010
shifted = x << 1
print(shifted) # Outputs 20
| Code Line | Explanation |
|---|---|
list1 = [1, 2, 3] |
Python requests memory from the OS and builds a PyListObject at
memory address 0xAA11. |
list2 = [1, 2, 3] |
Python requests FRESH memory and builds a completely identical
PyListObject at a different memory address 0xBB22.
|
list1 == list2 |
Python traverses both lists item by item, confirming every value inside matches
perfectly. This is an O(N) operation. Returns True. |
list1 is list2 |
Python completely ignores the values inside. It simply compares the memory
addresses 0xAA11 == 0xBB22. Because the pointers differ, it returns
False in O(1) time. |
Input: x = 5; y = 3; out = x & y
Transformation: Python passes both integers to the CPU's Arithmetic Logic Unit. The ALU converts 5 to binary (`0101`) and 3 to binary (`0011`). The `&` (AND) gate strictly compares the vertical columns. `1 and 1 = 1`. All other mismatched columns become `0`. Result is `0001`.
Output State: out = 1
When you type a + b, no magic happens. Python intercepts the +
symbol and literally translates it into a function call: a.__add__(b).
These are called Magic Methods (Dunder Methods). If you try to add two
incompatible objects like [1] + 2, Python attempts to call
list.__add__(2). The internal C-code for lists checks if the right-side
argument is also a list. If it isn't, the method returns a special hardcoded C-signal
called NotImplemented, and Python crashes your program with a
TypeError.
Because operators are just hidden function calls, you can overwrite them! (See Advanced OOP Section).
Bitwise Left Shift 10 << 1 visualization inside a 1-byte register:
Current Memory (10): 0 0 0 0 1 0 1 0
\ \ \ \
Shifted Left 1 Slot: 0 0 0 1 0 1 0 0 (Yields integer 20)
Shifting bits to the left mathematically multiplies by 2, and is drastically faster than CPU division formatting.
When using operators on Machine Learning structures like NumPy Arrays, the shapes MUST
align algebraically. Array(3, 3) + 5 works because Python "Broadcasts" the
scalar 5 into a matching (3, 3) geometrical lattice before firing the
__add__ methods.
All operators return brand new standard Python objects (int,
float, bool). They NEVER modify the original inputs. If you do
c = a + b, Python executes the math, creates a new memory block for the
result, and points c to it.
Float Precision Drift: What is 0.1 + 0.2 == 0.3?
It returns False!
Computers store memory in base-2 binary. The fraction 1/10 cannot be
perfectly represented in binary (just like 1/3 cannot be perfectly written in base-10;
it becomes 0.33333333...). 0.1 + 0.2 secretly evaluates in binary to
0.30000000000000004. Because the == operator requires a
perfect 100% binary match, it fails catastrophically.
Solution: In Data Science, never use == for Floats. You
must use math.isclose(a, b) which tolerates atomic drift.
Walrus Operator (:=): Introduced in Python 3.8. It assigns
a variable and returns it simultaneously. Highly used in while loops.
while (chunk := file.read(1024)): print(chunk)
This single line does three things: creates a variable `chunk`, executes a read command, AND checks if it evaluated to empty to terminate the loop.
Mistake: Chaining is with or incorrectly.
if type(x) is list or dict:
Why is this bad?: Python evaluates this as
(type(x) is list) or (dict). Because the class dict is
considered mathematically "Truthy" inherently, this whole expression always violently
evaluates to True constantly, destroying your logic.
Fix: if type(x) in (list, dict):
Identity checks (is) test pointer addresses instantly (O(1)).
Equality checks (==) on heavy nested dictionaries must recurse through every
leaf node to verify matches (O(N) depth penalty).
If you only care if two variables point to the exact same dataset object, always use
is for a massive performance boost.
Challenge: Fix this faulty checking line so it doesn't crash on null
sets: if len(my_list) > 0 and my_list[0] == 5:
Expected Answer: Python operators use Short-Circuit
Evaluation. If my_list is completely empty,
my_list[0] will throw an IndexError. But, because we placed
len(my_list) > 0 first, Python evaluates the left side of the `and`, sees
it is False, and instantly aborts the rest of the line without ever trying to
run the unsafe `my_list[0]` check! The code is actually already perfectly safe.
How does Python handle massive Exponentiation like 2 ** 10000 without
overflowing C-memory restrictions?
Unlike Java where integers crash out at 2.1 Billion, Python integers are not standard
4-byte int32 primitives. They are dynamic "Bignum" arrays hiding inside the
C-struct. If the number exceeds the computer's 64-bit threshold, Python quietly
allocates a second array block and chains them together, executing complex algorithmic
carries across the arrays. Python will let you calculate 10 ** 10000
endlessly until your physical stick of RAM runs out of space, making it perfect for
cryptography math.