Consider the following program:
class Foo(object): def __init__(self, bar): self.bar = bar def __repr__(self): return 'Foo(%r)' % (self.bar,) def __eq__(self, other): print('Foo.__eq__(%r, %r)' % (self, other)) return self.bar == other foo1 = Foo('A') foo2 = Foo('B') assert foo1 not in [foo2]
Under CPython 2.7.11 and 3.5.1, it prints:
Foo.__eq__(Foo('A'), Foo('B')) Foo.__eq__(Foo('B'), 'A')
But under PyPy 5.3.1 (2.7), it prints:
Foo.__eq__(Foo('B'), Foo('A')) Foo.__eq__(Foo('A'), 'B')
PyPy’s behavior contradicts the Python 2 language reference:
For the list and tuple types,
x in yis true if and only if there exists an index i such that either
x is y[i]or
x == y[i]is true.
because according to the data model:
According to the Python 3 language reference (can’t find anything about this in Python 2), equality does not have to be symmetric (emphasis mine):
User-defined classes that customize their comparison behavior should follow some consistency rules, if possible:
. . .
- Comparison should be symmetric
. . .
Python does not enforce these consistency rules. In fact, the not-a-number values are an example for not following these rules.
In my actual case,
y are instances of different classes with completely different, magical
__eq__, so this becomes important.