There appears to be an almost philosophical difference in the meaning of "equality" between C++ and Python. I became aware of this distinction through an attempt to do in Python something that is quite difficult in C++: distinguishing between two enum types when they are both just a wrapper for a set of integers, but the issue is broader than enums, hence the present question.
If I write in C++ code such as the following
#include <iostream>
struct Foo {
bool operator==(const Foo& foo) const { return this == &foo; }
};
struct Bar {};
int main() {
Foo foo = Foo();
Bar bar = Bar();
if (foo == bar) std::cout << "ok" << std::endl;
}
I fully expect that the equality comparison will fail. Indeed, it's a compilation error. You can't even compare two objects unless they're, just to get going, of the same type.
And yet it appears that "there is little (no?) precedent in Python for equality comparisons raising errors".
and "if [an object] raises every time it is compared to a [object of a different type], it will break any container it is added to".
Indeed, writing
class Foo(object):
pass
class Bar(object):
pass
foo = Foo()
bar = Bar()
if (foo == bar):
print("equal")
reveals that there is no problem in comparing objects that should otherwise be incomparable.
What, philosophically, is the root of this distinction in the meaning of equality between the two languages?
Update
Part of my puzzlement at finding this out about Python is that so far every feature appears to have been designed with the intent of being "natural", "intuitive", even "human"—not that these can be defined in the first place.
But consider that you are at the fruit section of a grocery shop and ask one of the aproned chaps: "Could you tell me whether these oranges are Fuji or Red Delicious?" Surely no one could make sense of the question to venture an answer one way or the other. So the question is how to provide a response of "incredulous" in bits and bytes.
Update 2
(Too long to be a comment to @GiacomoAlzetta's comment) I respect your opinion. Still, from this point on, I will not respect a book on Python that does not dedicate a chapter, or at least a section, to pointing out that 3 < [1]
is True, and that explains the background (whether historical or philosophical) for this. Being dynamically typed does not mean that one is so very cornered (because, e.g., one has only a handful of available names 'a', 'b', and 'c') to reuse a name for a very different meaning. Ultimately, it's not even a philosophical, but an engineering, issue: How do you remove, or at least reduce, the chance that one among multiple people collaborating on a software project will introduce bugs in the system? Bugs that remain dormant (because the language is dynamically typed—and we cannot predict computation paths) are far worse than bugs that scream "error".
The fundamental intent in Python has changed over time. At the start, and until late in the development of Python 2, it was essentially the case that any two objects could be compared. Even mixed-type comparisons that made no intuitive sense, like:
>>> 3 < [1]
True
In cases like that, it was actually the string names of the types that were compared, and the result above was due to that, as strings, "int" < "list"
. This was driven mostly by a misguided (in hindsight) attempt to impose a total ordering on all objects.
Looking ahead to Python 3, the intent changed, and started to be implemented with the datetime
module types (which were introduced in Python 2): mixed-typed <
, <=
, >
, and >=
comparisons that made scant intuitive sense were to raise exceptions instead, while senseless mixed-type ==
would always return False
and senseless mixed-type !=
always True
.
>>> import datetime
>>> n = datetime.datetime.now()
>>> n < 3
Traceback (most recent call last):
...
TypeError: can't compare datetime.datetime to int
>>> n >= 3
Traceback (most recent call last):
...
TypeError: can't compare datetime.datetime to int
>>> n == 3
False
>>> n != 3
True
Python 3 strives to act "like that" generally. Of course there are exceptions. For example, when comparing objects of different numeric types (like integers and floats), they're usually coerced to a common type under the covers.
I was deeply involved in these decisions (and wrote the datetime
module), so you can trust me on that. But as to what C++
intends, you'll have to ask someone else ;-)
I should add that, up until the introduction of "rich comparisons" in Python 2, all comparisons funneled through a cmp()
protocol: in the CPython implementation, comparing two objects returned one of the integers in [-1, 0, 1]
. __cmp__()
methods had no idea which of <
, <=
, ==
, !=
, >
, and >=
was actually desired. So while the "compare any two things no matter what" original design was disliked early on, it was technically difficult to get away from before rich comparisons were incorporated into the language. Then it became straightforward (if tedious).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With