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