It’s worth noting that the comment about non-transitive equality really is more a consequence of Python’s chained binary operators. In Python, if OP1 and OP2 are binary operators, then the expression x OP1 y OP2 z is defined as exactly equivalent to (x OP1 y) and (y OP2 z), but with a guarantee that y is only evaluated once. This means that the operands on the two “ends” of the chain are never compared to each other, and that any comparison behavior between them is never considered.
And since Python has operator overloading, it’s possible to define types which use different logic when comparing to values of different types. As in this case, where OrderedDict checks only keys/values when comparing to dict but checks keys, values and order when comparing to another OrderedDict.
It’s even possible to define behavior so that values of distinct types are interchangeable in various operations; for example, any two values which compare equal via == and which define __hash__() in a way that produces the same result will be treated as equivalent when used as keys. So:
>>> d = {0: 'foo'}
>>> print(d[0.0])
foo
This works because 0 == 0.0 and hash(0) == hash(0.0).
Transitivity for a relation R simply means x R y ∧ y R z ⇒ x R z which normally holds for equality. The way Python chains comparison operators is thus not the issue here, the issue is that OrderedDict.__eq__ is not transitive while normally equality is.
I’m aware of the definition of transitivity, thanks.
However, the specific example given is cited as confusing because the chain of operators succeeds when it “shouldn’t”, and I was giving the reason why it is that way, which is, as I said, due to the way Python handles operator chaining (i.e., in x == y == z, x and z are never compared to each other). And the expectation that == should always be transitive is generally not a good one to have – equality comparisons can be difficult and complex depending on the domain one is working with.
I was just quoting the definition to show that transitivity is seperate from the chaining of operators, my apologies if it came across as condescending.
I still fail to see how the chaining of operators matters for this example. The only reason why people would expect x == z in x == y == z is precisely because of transitivity. If you look at another example of chaining of operators like x < y > z it’s obvious that there is ofcourse no direct comparison between x and z.
I am aware that in a lot of domains (including Python as demonstrated here) there are a lot of exceptions to the transitivity of equality and that transitive equality is hard to achieve. However that does not mean that it should not be the goal to get as close to it as possible to avoid confusion.
Speaking of non-rransitive equality, it’s also worth noting that equality over floats is non-transitive in general. This is as a results of floats being a necessarily imperfect representation of the reals, where equality is transitive by definition.
The comments were a fun read, lol
A case study in How Not to Disagree with Someone
It’s worth noting that the comment about non-transitive equality really is more a consequence of Python’s chained binary operators. In Python, if
OP1
andOP2
are binary operators, then the expressionx OP1 y OP2 z
is defined as exactly equivalent to(x OP1 y) and (y OP2 z)
, but with a guarantee thaty
is only evaluated once. This means that the operands on the two “ends” of the chain are never compared to each other, and that any comparison behavior between them is never considered.And since Python has operator overloading, it’s possible to define types which use different logic when comparing to values of different types. As in this case, where
OrderedDict
checks only keys/values when comparing todict
but checks keys, values and order when comparing to anotherOrderedDict
.It’s even possible to define behavior so that values of distinct types are interchangeable in various operations; for example, any two values which compare equal via
==
and which define__hash__()
in a way that produces the same result will be treated as equivalent when used as keys. So:This works because
0 == 0.0
andhash(0) == hash(0.0)
.Transitivity for a relation
R
simply meansx R y ∧ y R z ⇒ x R z
which normally holds for equality. The way Python chains comparison operators is thus not the issue here, the issue is thatOrderedDict.__eq__
is not transitive while normally equality is.I’m aware of the definition of transitivity, thanks.
However, the specific example given is cited as confusing because the chain of operators succeeds when it “shouldn’t”, and I was giving the reason why it is that way, which is, as I said, due to the way Python handles operator chaining (i.e., in
x == y == z
,x
andz
are never compared to each other). And the expectation that==
should always be transitive is generally not a good one to have – equality comparisons can be difficult and complex depending on the domain one is working with.I was just quoting the definition to show that transitivity is seperate from the chaining of operators, my apologies if it came across as condescending.
I still fail to see how the chaining of operators matters for this example. The only reason why people would expect
x == z
inx == y == z
is precisely because of transitivity. If you look at another example of chaining of operators likex < y > z
it’s obvious that there is ofcourse no direct comparison betweenx
andz
.I am aware that in a lot of domains (including Python as demonstrated here) there are a lot of exceptions to the transitivity of equality and that transitive equality is hard to achieve. However that does not mean that it should not be the goal to get as close to it as possible to avoid confusion.
Speaking of non-rransitive equality, it’s also worth noting that equality over floats is non-transitive in general. This is as a results of floats being a necessarily imperfect representation of the reals, where equality is transitive by definition.
Yeah, transitive equality is actually a lot rarer in practice than people seem to expect.