builtin max() and weak ordering

Stephen Evans stephen at recombinant.co.uk
Thu Mar 3 05:38:26 EST 2011


The CPython builtins min() and max() both return the first satisfactory object found. This behaviour is undocumented. Examining the source in bltinmodule.c shows that min() and max() both result in a call to min_max() with Py_LT and Py_GT passed respectively.

The behaviour of min() is correct. But with weak orderings of equivalent objects, surely max() should be using Py_GE ? (the converse of Py_LT). Should I be reporting a bug, submitting a patch, making feature request or suggesting a documentation update ?
    
For a more mathematical consideration (not casual reading):

Stepanov, Alexander and Paul McJones. 2009. Elements of Programming. Addison Wesley. Pages 52-53

The code below demonstrates the issue. Using the total key gives the correct result. Using the weak key returns the "incorrect" result. Tested with Python 2.7.1 and 3.1.2 (applies to 3.2)

Stephen Evans



from __future__ import print_function
from operator import attrgetter

class Pair():
    def __init__(self, m0, m1):
        self.m0, self.m1 = m0, m1

    # rich comparisons are not used
    def __lt__(self, other): raise NotImplementedError
    def __le__(self, other): raise NotImplementedError
    def __eq__(self, other): raise NotImplementedError
    def __ne__(self, other): raise NotImplementedError
    def __gt__(self, other): raise NotImplementedError
    def __ge__(self, other): raise NotImplementedError
        
    def __repr__(self):
        """repr() as a tuple"""
        return repr((self.m0, self.m1))
        
    @property
    def total(self):
        return (self.m0, self.m1)

    @property
    def weak(self):
        return self.m0


def test():
    """
    demonstrate the failure of the builtin max() to respect the original order
    of equivalent objects.
    """
    r = [ (0, 1), (0, 2) ]
    r = [ Pair(*p) for p in r ]
    
    # verify total and weak ordering
    assert r == sorted(r, key=attrgetter('weak')) == sorted(r, key=attrgetter('total'))
    print(r)

    # from the sorted list
    print(r[0], r[-1])

    # using total ordering
    print(min(r, key=attrgetter('total')), max(r, key=attrgetter('total')))

    # using weak ordering, builtin_min and builtin_max functions in bltinmodule.c
    #   min works as expected using Py_LT
    #   max uses Py_GT rather than the converse of Py_LT (which would be Py_GE)
    print(min(r, key=attrgetter('weak')), max(r, key=attrgetter('weak')))
    
test()



More information about the Python-list mailing list