Comparisons and sorting of a numeric class....

Chris Angelico rosuav at
Wed Jan 7 05:01:27 CET 2015

On Wed, Jan 7, 2015 at 2:39 PM, Dave Angel <davea at> wrote:
> Here we need help from someone who knows more about this particular detail.
> I don't know which special methods bool() or not will call, but I'd think it
> would be something with a better name than __nonzero__

In Python 2, it is indeed __nonzero__, but in Python 3, it's __bool__:

Python 2.7.3 (default, Mar 13 2014, 11:03:55)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class X:
...  def __nonzero__(self): print("called")
>>> bool(X())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __nonzero__ should return an int

Python 3.5.0a0 (default:1c51f1650c42+, Dec 29 2014, 02:29:06)
[GCC 4.7.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class X:
...  def __bool__(self): print("called")
>>> bool(X())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __bool__ should return bool, returned NoneType

Also, as you can see, Py2's method allows any integer to be returned,
while Py3's expects a bool. The boolification of an object MUST result
in either True or False, nothing else.

>> Question:  If two different class's instances are being compared by
>> '==', and both define an __eq__ method, which method gets called?  ( I
>> don't know if that applied here... but I'm not familiar with order of
>> operations )
> The left object's methods are generally examined first.  So if you said
>     mylist * 5
> you'd be using a __mul__ method of list class.

Order of operations has nothing to do with this; that's to do with
operator precedence/associativity, which is what stipulates that all
of these parentheses are unnecessary:

a + (b * c)
(a + b) + c
a ** (b ** c)
a or (b and c)
(a + b) in c
a if (b + c) else d

The operator precedence tables neither know nor care what the objects
in question are. But once the interpreter gets to the next step, and
starts actually figuring out the value of the expression, then it
usually goes for the left object's methods first; if that object
returns NotImplemented, it'll try the right object (sometimes with a
reflected method):

>>> class X:
...  def __eq__(self, other): print("Am I equal to:",other)
...  def __add__(self, other): print("me +",other)
...  def __radd__(self, other): print(other,"+ me")
>>> X() == 1
Am I equal to: 1
>>> 2 == X()
Am I equal to: 2
>>> X() + 3
me + 3
>>> 4 + X()
4 + me

>> But I don't think that stops me from treating the sort order of all
>> items which are quasi-equal, as a second search (hierarchical search).
>> eg: Sort first by definite magnitude, then among those deemed 'equal',
>> sort them by average expectation value...  That's going to be good
>> enough for me and my users, for the sort order of truly quasi equal
>> things is arbitrary anyway... as long as it's not 'defintely wrong'
>> they'll have no reason to complain.
> You could write a sort that works that way, but I don't think you can assume
> that the built-in sort will.  sort assumes that the comparison works in a
> certain way, and if it doesn't, the symptoms could range from random
> ordering (including items drastically out of order) to a possibly never
> terminating sort.

If you can separate the value into two parts, magnitude and
expectation, you could have a key function that makes them sortable:

def mag_exp(val):
    if not isinstance(val, MagicFloat): return (val, None)
    return int(val), val-int(val) # example

srtlst = sorted(lst, key=mag_exp)

That'll sort all elements by the first value in the tuple, and if
they're identical on that, by the second value. That's the easiest way
to do a multi-part sort.


More information about the Python-list mailing list