On Sat, 9 May 2020 03:01:15 +1000
Steven D'Aprano
On Thu, May 07, 2020 at 11:04:16AM -0400, Dan Sommers wrote:
On Thu, 7 May 2020 21:18:16 +1000 Steven D'Aprano
wrote: The strongest equality is the "is" operator
Please don't encourage the conceptual error of thinking of `is` as *equality*, not even a kind of equality. It doesn't check for equality, it checks for *identity* and we know that there is at least one object in Python where identical objects aren't equal:
py> from math import nan py> nan is nan True py> nan == nan False
We'd better agree to disagree on this one.
Why? In what way is there any room for disagreement at all?
I believe that the "is" operator is a test for some kind of equality, and you apparently don't.
* some equal objects are not identical; * and some identical objects are not equal.
It is a matter of fact that in Python `is` tests for object identity, not equality:
Section 6.10 is entitled Comparisons, and lists both "is" and "==" as comparison operators. I admit that my use of the word "strongest" (to describe the "is" operator) and my conceptual ordering of different kinds of equality fails in the light of NaNs. Curse you, IEEE Floating Point! :-) Then again, that same documentation states "User-defined classes that customize their comparison behavior should follow some consistency rules, if possible." One of the consistency rules is "Equality comparison should be reflexive. In other words, identical objects should compare equal," and that rule is summarized as "x is y implies x == y." So I'm not the only one who thinks of "is" as a kind of equality. :-)
You don't even need to look at such exotic objects as NANs to see that `is` does not test for equality. None of these will return True:
[] is [] 1.5 is Fraction(3, 2) (a := {}) is a.copy()
even though the operands are clearly equal.
The OP wants [1, 2, 3] == (1, 2, 3) to return True, even though the operands are clearly not equal.
YAGNI is how I feel about an operator that compares sequences element by element.
Remember that list-to-list and tuple-to-tuple already perform the same sequence element-by-element comparison.
My mistake. I should have said "... compares arbitrary sequences of varying types ..." and not just "sequences."
All this proposal adds is *duck-typing* to the comparison, for when it doesn't matter what the container type is, you care only about the values in the container. Why be forced to do a possibly expensive (and maybe very expensive!) manual coercion to a common type just to check the values for equality element by element, and then throw away the coerced object?
Then I'll write a function that iterates over both sequences and compares the pairs of elements. There's no need to coerce one or both completes sequences.
If you have ever written `a == list(b)` or similar, then You Already Needed It :-)
I don't recall having written that. I do end up writing 'a == set(b)' when a is a set and b is a list, rather than building b as a set in the first place, but sets aren't sequences.
FWIW, I agree: list != tuple. When's the last time anyone asked for the next element of a tuple?
Any time you have written:
for obj in (a, b, c): ...
you are asking for the next element of a tuple.
I have been known to write: for x in a, b, c: (without the parenthesis), usually in the REPL, but only because it's convenient and it works. In other programming languages that don't allow iteration over tuples, I use lists instead.