Python: Usable Max and Min values

Question:

Python 2.x allows heterogeneous types to be compared.

A useful shortcut (in Python 2.7 here) is that None compares smaller than any integer or float value:

>>> None < float('-inf') < -sys.maxint * 2l < -sys.maxint
True

And in Python 2.7 an empty tuple () is an infinite value:

>>> () > float('inf') > sys.maxint
True

This shortcut is useful when one might sort a mixed list of ints and floats and want to have an absolute minimum and maximum to reference.

This shortcut has been removed in Python 3000 however (this is Python 3.2):

>>> None < 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: NoneType() < int()

Furthermore, Python3000 has removed sys.maxint on the theory that all ints promote to longs and the limit no longer applies.

PEP 326, A Case for Top and Bottom Values, advanced a reference min and max in Python. The new ordering behavior documented.

Since PEP 326 was rejected, what are useful, useable definitions for a min and max value that work with integers and floats and longs on Python 2X and Python 3000?

Edit

Several answers are along the lines of “just use maxv=float(‘inf’)”… The reason I am thinking, however remote the possibility, is this:

>>> float(2**5000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: long int too large to convert to float 

And:

>>> cmp(1.0**4999,10.0**5000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')

Yet:

>>> () > 2**5000
True

In order to cmp to a float value, float('inf'), the long value would need to be converted to a float and the conversion would cause an OverflowError

Conclusion

Thank you everyone for your answers and comments. I picked TryPyPy’s answer because it seemed most inline with what I was asking: an absolute greatest and absolute least value as described in the Wikipedia entry on infinity.

With this question, I learned that a long or int value is not converted to a float in order to complete the comparison of float('inf') > 2**5000. I did not know that.

Asked By: the wolf

||

Answers:

You have the most obvious choices in your question already: float('-inf') and float('inf').

Also, note that None being less than everything and the empty tuple being higher than everything wasn’t ever guaranteed in Py2, and, eg, Jython and PyPy are perfectly entitled to use a different ordering if they feel like it. All that is guaranteed is consistency within one running copy of the interpreter – the actual order is arbitrary.

Answered By: lvc

For numerical comparisons, +- float("inf") should work.

It doesn’t always work (but covers the realistic cases):

print(list(sorted([float("nan"), float("inf"), float("-inf"), float("nan"), float("nan")])))
# NaNs sort above and below +-Inf
# However, sorting a container with NaNs makes little sense, so not a real issue.

To have objects that compare as higher or lower to any other arbitrary objects (including inf, but excluding other cheaters like below), you can create classes that state their max/min-ness in their special methods for comparisons:

class _max:
    def __lt__(self, other): return False
    def __gt__(self, other): return True

class _min:
    def __lt__(self, other): return True
    def __gt__(self, other): return False

MAX, MIN = _max(), _min()

print(list(sorted([float("nan"), MAX, float('inf'), MIN, float('-inf'), 0,float("nan")])))
# [<__main__._min object at 0xb756298c>, nan, -inf, 0, inf, nan, <__main__._max object at 0xb756296c>]

Of course, it takes more effort to cover the ‘or equal’ variants. And it will not solve the general problem of being unable to sort a list containing Nones and ints, but that too should be possible with a little wrapping and/or decorate-sort-undecorate magic (e.g. sorting a list of tuples of (typename, value)).

Answered By: TryPyPy

In cPython, the cmp does not perform a conversion to float implicitly. i.e., this works:

>>> float('inf') > 2**5000
True

While this explicitly performs the dread conversion:

>>> float('inf') > float(2**5000)
Overflow...

The correct answer, IMHO, is not a value per se by a change in logic:

def func_with_min():
   minval=None
   for loop in list_with_mins:
      if minval is None or minval<minseen:
          # do that min thing you wanna do...

If you want to have a value then float('-inf') for min and float('inf') is pretty safe. Be sure to cache that outside of a loop however:

def func():
   minval=float('-inf')
   for loop in now_you_can_loop:
       # otherwise float('-inf') is kinda slow
Answered By: user648852
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.