efthimio
efthimio

Reputation: 918

How does Python determine operand precedence for ==?

Comparing lists with the == operator returns a bool.

>>> [0,0,0] == [0,1,0]
False

Comparing numpy arrays with the == operator returns another array resulting from element wise comparison.

>>> np.array([0,0,0]) == np.array([0,1,0])
array([ True, False,  True])

If I mix operand types, the numpy method always takes precedence.

>>> np.array([0,0,0]) == [0,1,0]
array([ True, False,  True])
>>> [0,0,0] == np.array([0,1,0])
array([ True, False,  True])

How does Python determine which operand has precedence? My question has more to do with the mechanics of Python than with numpy or list.

Upvotes: 1

Views: 692

Answers (2)

Yash Patidar
Yash Patidar

Reputation: 11

In Python, the left operand is always evaluated before the right operand. That also applies to function arguments.

Python uses short circuiting when evaluating expressions involving the and or or operators. When using those operators, Python does not evaluate the second operand unless it is necessary to resolve the result. That allows statements such as if (s != None) and (len(s) < 10): ... to work reliably.

year % 4 == 0 and year % 100 != 0 or year % 400 == 0

Upvotes: 1

ShadowRanger
ShadowRanger

Reputation: 155418

The rich comparison operators always ask the left operand first (unless the right operand is an instance of a subclass of the type of the left; not the case here). If the first operand checked returns NotImplemented (which list will always do when comparing to numpy arrays, since it can't possibly have knowledge of them baked in to the core interpreter), then the second operand's is asked to perform the reflected comparison (which doesn't change for __eq__).

Since numpy baked knowledge of list into its __eq__, and list didn't bake in knowledge of numpy arrays, numpy either goes first and uses its own logic immediately (when it's the left operand) or list goes first (when it's the left operand), gives up by returning NotImplemented, then numpy's __eq__ makes the final call.

Rough internals of left == right call (omitting subclass special case):

attempt = type(left).__eq__(left, right)
if attempt is NotImplemented:
   attempt = type(right).__eq__(right, left)
   if attempt is NotImplemented:
       attempt = False  # Specific to __eq__, if both can't compare, returns False
return attempt

For full details, start from the NotImplemented docs and follow the yellow brick road (read: links).

Upvotes: 3

Related Questions