2

I've got a head scratchier and it seems I'm not the only one, but is there really no solution? I find that hard to believe!

So the question is why can't I call int.__eq__ with 2 operators or i.__eq__ with one? How can I use __eq__ (and the other comparison operators) for a per item comparison for a sequence of ints?

Here's a dump from python2.7.17:

>>> i = 0
>>> type(i)
<type 'int'>
>>> i.__eq__(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__eq__'
>>> type(i).__eq__(i, 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected 1 arguments, got 2
>>> type(i).__eq__(0)
NotImplemented

But my dumo from python3.6.9 behaves itself:

>>> i = 0
>>> type(i)
<class 'int'>
>>> i.__eq__(0)
True
>>> type(i).__eq__(i, 0)
True
>>> type(i).__eq__(0)  # this is not expected to work, but just for the sake of voodoo.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected 1 arguments, got 0

I know python2 is no longer supported but there are a few applications that only use python2 and I would like to make my code backwards compatible anyway.

So anyone out there have a solutuon for hacking the comparison magic operator methods as function calls in python2? I am sure there must be some work around.

It seems there is some information on this. I just read that python2 falls back to using cmp in some cases, while in python3 there is no cmp (or so I read). So I guess the thing to do is not use eq and ne but instead use cmp but I love some additional perspective on this

Brad Solomon
  • 38,521
  • 31
  • 149
  • 235
Ismael Harun
  • 143
  • 2
  • 7
  • Note that `int.__eq__` is actually a bound method (actually, a `method-wrapper`) produced by passing `int` to `type.__eq__`, rather than the unbound `__eq__` defined by `int` (as it is in Python 3). `int.__eq__(int) == True`, `int.__eq__(float) == False`, `int.__eq__(3)` returns `NotImplemented` because you can't compare types and `int` values. – chepner Jun 05 '20 at 12:11
  • (You get some hint of this by comparing the return value of `type.__eq__.__get__(int, type)` to `int.__eq__`. I'm not entirely sure `int.__eq__` actually causes `type.__eq__.__get__` to be called, but the both appear to be accessing or creating the same instance of `method-wrapper`.) – chepner Jun 05 '20 at 12:22
  • Well I clicked answer my own question but it wouldn't let me and closed my question. Not sure why when I didn't get to post my answer. So I will put it here. – Ismael Harun Jun 05 '20 at 12:28
  • So I found an an answer. Yes indeed it seems python2 has it reasons for diverting functionality from the magic comparison operators. Instead it uses __cmp__ or cmp. So the solution is rather simple and not that in elegant. – Ismael Harun Jun 05 '20 at 12:29
  • SPECIAL_OPNAMES = \ { '__eq__': (lambda *args, **kwargs: not cmp(*args, **kwargs)) \ , '__ne__': (lambda *args, **kwargs: cmp(*args, **kwargs)) \ } if sys.version_info.major == 2 else \ {} – Ismael Harun Jun 05 '20 at 12:30
  • item = 1 opname = '__eq__' t = type(item) op = SPECIAL_OPNAMES[opname] if opname in SPECIAL_OPNAMES else getattr(t, opname) op(item, 1) # result is True – Ismael Harun Jun 05 '20 at 12:31
  • Voted to reopen; the duplicated question explains how `==` works, but doesn't answer why `int.__eq__` didn't seem to work as expected. – Snild Dolkow Jun 05 '20 at 12:32

1 Answers1

2

The general rule is: don't touch dunderscore methods, use functions and operators instead and those will delegate to dunderscore implementation as necessary. In your case you're looking for the == operator or the functional equivalent operator.eq.

from operator import eq
from functools import partial

print eq(1, 2)

f = partial(eq, 1)
print f(2)
deceze
  • 510,633
  • 85
  • 743
  • 889