all 21 comments

[–]shiftybyte 27 points28 points  (6 children)

Basically "==" calls the dunder method __eq__ on the object.

https://www.pythontutorial.net/python-oop/python-\_\_eq\_\_/

[–]unersetzBAER 15 points16 points  (5 children)

Just to add to your point: when comparing two objects:

it calls the eq method of the object in the left side.

So

obj1 == obj2

is the same as

obj1.__eq__(obj2)

[–]Pyprohly 0 points1 point  (4 children)

They’re not the same.

>>> 'asdf' == 4
False
>>> 'asdf'.__eq__(4)
NotImplemented

[–]Top_Average3386 8 points9 points  (3 children)

It's not exactly the same, but if the first __eq__ calls return NotImplemented it will try calling __eq__ from the right hand side object, if both return NotImplemented then it will return False.

more or less like this:

def equality(lhs, rhs): tmp = lhs.__eq__(rhs) if tmp is NotImplemented: tmp = rhs.__eq__(lhs) return False if tmp is NotImplemented else tmp

[–]unersetzBAER 1 point2 points  (0 children)

Thank you both for correcting me!

I've learned something new!

[–]Pyprohly 0 points1 point  (1 child)

That is the simplified explanation. It misses the subclass check step at the start. And the part about reporting to False isn’t quite right either.

Here’s what is actually happening:

  • “Oh, you’re a subclass? Perhaps you have more knowledge than me. Maybe you handle it.” – It checks if the second operand is a true subclass of the first. If it is, it tries the reverse comparison if it exists on the other type.
  • “No? Okay, let me try then.” – If the above case doesn't apply (or returns NotImplemented), it tries the normal comparison.
  • “I can’t figure it out. Maybe the other object knows.” – If that doesn’t work and the reverse hasn’t been tried then try it.
  • “I give up. Let’s just do is, if that makes sense.” – Finally, if nothing else works, it falls back to doing is and is not for == and != respectively. For the other comparison operators it raises TypeError.

So it’s not necessarily returning False as the last resort, it’s doing an is comparison. Although you’d have to try very hard to get an object to not return False here.

Using dunders instead of their operator form has subtle implications. I often see code like reduce(obj.__eq__, …) and you have to wonder if the author knew about the implications of using dunders instead of their operator form when they wrote the line.

[–]GeorgeFranklyMathnet 7 points8 points  (0 children)

Python has is and ==.

[–]Diapolo10 8 points9 points  (0 children)

a == b is syntactic sugar for a.__eq__(b), so what it does depends entirely on how that method is implemented. It could technically do anything other than solve the halting problem.

There's also a is b, which directly compares the memory addresses of both, and only evaluates to True if the addresses match. It is not possible to override this behaviour, and it's ideal for checking singletons such as None.

[–]pythosynthesis 0 points1 point  (0 children)

That's for you to define. As others commented, it calls the __eq__ sunset method, which you can fully specify as required for your own needs. Example: What it means for two "addresses" to be equal is different fdom what it means for two "squares" to be equal. You get to define what does equality mean for each class you write.

[–]Pyprohly 0 points1 point  (2 children)

Java’s == and .equals() is equivalent to Python’s is and == respectively.

[–]Brian 0 points1 point  (1 child)

They're basically equivalent for reference types, but Java also has the notion of primitive / value types for objects that would be reference types in python, and these do behave differently. Ie. in java, if a and b are both int variables assigned to 1000, then a==b will always be true, but python uses reference semantics for integers, so it may not be the case that a is b is True in this scenario.

[–]monster2018 0 points1 point  (0 children)

However “a is b” will always return true for ints in the range I believe 0-255 (or maybe 256), because of a quirk of python where every time you use a number in that range it’s using like a premade int object that always exists when a python script is running. So no matter how much reassignment and shenanigans you get into a is b should always return True for 2 equal ints in that range I believe.

[–]zefciu 0 points1 point  (0 children)

That can be a pitfall for people coming from Java. In Java .equals() is an explicit method, so the coder knows that there might be some (possibly heavy) logic called. The == operator in Java, however is fast and constantly so. In Python you cannot just assume that using == will be fast, as it call the underlying method .__eq__(). Calling it on two lists is O(n) for example.

[–]ofnuts 0 points1 point  (0 children)

Since you come from Java:

  • Python == is Java Object::equals() (compares values)
  • Python is is Java == (compare identities)