On Mon, Mar 5, 2012 at 4:41 AM, Mark Shannon <m...@hotpy.org> wrote: > Comparing two objects (of the same type for simplicity) > involves a three stage lookup: > The class has the operator C.__eq__ > It can be applied to operator (descriptor protocol): C().__eq__ > and it produces a result: C().__eq__(C()) > > Exceptions can be raised in all 3 phases, > but an exception in the first phase is not really an error, > its just says the operation is not supported. > E.g. > > class C: pass > > C() == C() is False, rather than raising an Exception. > > If an exception is raised in the 3rd stage, then it is propogated, > as follows: > > class C: > def __eq__(self, other): > raise Exception("I'm incomparable") > > C() == C() raises an exception > > However, if an exception is raised in the second phase (descriptor) > then it is silenced: > > def no_eq(self): > raise Exception("I'm incomparable") > > class C: > __eq__ = property(no_eq) > > C() == C() is False. > > But should it raise an exception? > > The behaviour for arithmetic is different. > > def no_add(self): > raise Exception("I don't add up") > > class C: > __add__ = property(no_add) > > C() + C() raises an exception. > > So what is the "correct" behaviour? > It is my opinion that comparisons should behave like arithmetic > and raise an exception.
I think you're probably right. This is one of those edge cases that are so rare (and always considered a bug in the user code) that we didn't define carefully what should happen. There are probably some implementation-specific reasons why it was done this way (comparisons use a very different code path from regular binary operators) but that doesn't sound like a very good reason. OTOH there *is* a difference: as you say, C() == C() is False when the class doesn't define __eq__, whereas C() + C() raises an exception if it doesn't define __add__. Still, this is more likely to have favored the wrong outcome for (2) by accident than by design. You'll have to dig through the CPython implementation and find out exactly what code needs to be changed before I could be sure though -- sometimes seeing the code jogs my memory. But I think of x==y as roughly equivalent to r = NotImplemented if hasattr(x, '__eq__'): r = x.__eq__(y) if r is NotImplemented and hasattr(y, '__eq__'): r = y.__eq__(x) if r is NotImplemented: r = False which would certainly suggest that (2) should raise an exception. A possibility is that the code looking for the __eq__ attribute suppresses *all* exceptions instead of just AttributeError. If you change no_eq() to return 42, for example, the comparison raises the much more reasonable TypeError: 'int' object is not callable. -- --Guido van Rossum (python.org/~guido) _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com