105

Evaluating,

max_val = max(a)

will cause the error,

ValueError: max() arg is an empty sequence

Is there a better way of safeguarding against this error other than a try, except catch?

a = []
try:
    max_val = max(a)
except ValueError:
    max_val = default 
jfs
  • 399,953
  • 195
  • 994
  • 1,670
Alexander McFarlane
  • 10,643
  • 9
  • 59
  • 100
  • 12
    [*The `default` argument specifies an object to return if the provided iterable is empty.*](https://docs.python.org/3/library/functions.html#max) – vaultah Mar 22 '16 at 15:05

7 Answers7

165

In Python 3.4+, you can use default keyword argument:

>>> max([], default=99)
99

In lower version, you can use or:

>>> max([] or [99])
99

NOTE: The second approach does not work for all iterables. especially for iterator that yield nothing but considered truth value.

>>> max(iter([]) or 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: max() arg is an empty sequence
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • 1
    Another option for pre-3.4 would be to create a `my_max` function that mimics the newer behavior. – Steven Rumbalski Mar 22 '16 at 15:15
  • 1
    `max(iter([]) or 0)` is precisely what gave me the grief in the first place... Do you have any further suggestions on what do do with it? Secondly it never occurred to me to check the docs... I assumed such a simple function was a one-in-one-out process – Alexander McFarlane Mar 22 '16 at 15:24
  • @AlexanderMcFarlane, Duncan suggested a nice alternative. Check it out. – falsetru Mar 22 '16 at 15:28
  • @AlexanderMcFarlane, I'm not a native speaker. I don't understand what `one-in-one-out process`. Does it mean a common task? – falsetru Mar 22 '16 at 15:31
  • @falsetru by one-in-one-out I mean one argument in and one return value :) – Alexander McFarlane Mar 22 '16 at 16:18
  • Never knew the `default` kwarg exists! Not shown in the signature provided by PyCharm, only to find it in the documentation. – SnzFor16Min Aug 29 '23 at 09:05
12

In versions of Python older than 3.4 you can use itertools.chain() to add another value to the possibly empty sequence. This will handle any empty iterable but note that it is not precisely the same as supplying the default argument as the extra value is always included:

>>> from itertools import chain
>>> max(chain([42], []))
42

But in Python 3.4, the default is ignored if the sequence isn't empty:

>>> max([3], default=42)
3
Duncan
  • 92,073
  • 11
  • 122
  • 156
  • is there any reason to use `chain` over simple list concatenation e.g. `max( [42], [] )` where the default is `42`? – MajorInc Sep 09 '18 at 17:30
  • @MajorInc calling `max` with multiple arguments has a different meaning than calling it with a single argument. With a single iterable argument `max` will return the largest element it contains, but with multiple arguments it compares them directly (i.e. it won't iterate). e.g. `max([42], [])` is a list `[42]` but `max(chain([42], []))` and `max([], default=42)` both give `42`. – Duncan Sep 10 '18 at 08:13
2

The max of an empty sequence "should" be an infinitely small thing of whatever type the elements of the sequence have. Unfortunately, (1) with an empty sequence you can't tell what type the elements were meant to have and (2) there is, e.g., no such thing as the most-negative integer in Python.

So you need to help max out if you want it to do something sensible in this case. In recent versions of Python there is a default argument to max (which seems to me a misleading name, but never mind) which will be used if you pass in an empty sequence. In older versions you will just have to make sure the sequence you pass in isn't empty -- e.g., by oring it with a singleton sequence containing the value you'd like to use in that case.

[EDITED long after posting because Yaakov Belch kindly pointed out in comments that I'd written "infinitely large" where I should have written "infinitely small".]

Gareth McCaughan
  • 19,888
  • 1
  • 41
  • 62
  • It's counter-intuitive: the max of an empty list should be infinitely small (so that max(x,list...)=max(x,max(list...))) and the min of an empty list should be infinitely large. – Yaakov Belch Dec 04 '16 at 19:00
  • Whoops, yes, I wrote that the wrong way around; my apologies. I will fix my answer and credit you for spotting the mistake. [... Done. Thanks again.] – Gareth McCaughan Dec 04 '16 at 23:46
2

Another solution could be by using ternary operators:

nums = []
max_val = max(nums) if nums else 0

or

max val = max(iter(nums) if nums else [0])
Kurohige
  • 1,378
  • 2
  • 16
  • 24
2

Can create simple lambda to do this:

get_max = lambda val_list: max([ val for val in val_list if val is not None ]) if val_list else None

You can call it this way:
get_max(your_list)

Zeeshan
  • 1,187
  • 1
  • 9
  • 12
1
_DEFAULT = object()

def max_default(*args, **kwargs):
    """
    Adds support for "default" keyword argument when iterable is empty.
    Works for any iterable, any default value, and any Python version (versions >= 3.4
    support "default" parameter natively).

    Default keyword used only when iterable is empty:

    >>> max_default([], default=42)
    42

    >>> max_default([3], default=42)
    3

    All original functionality is preserved:

    >>> max_default([])
    Traceback (most recent call last):
    ValueError: max() arg is an empty sequence

    >>> max_default(3, 42)
    42
    """

    default = kwargs.pop('default', _DEFAULT)
    try:
        return max(*args, **kwargs)
    except ValueError:
        if default is _DEFAULT:
            raise
        return default

Bonus:

def min_default(*args, **kwargs):
    """
    Adds support for "default" keyword argument when iterable is empty.
    Works for any iterable, any default value, and any Python version (versions >= 3.4
    support "default" parameter natively).

    Default keyword used only when iterable is empty:

    >>> min_default([], default=42)
    42

    >>> min_default([3], default=42)
    3

    All original functionality is preserved:

    >>> min_default([])
    Traceback (most recent call last):
    ValueError: min() arg is an empty sequence

    >>> min_default(3, 42)
    3
    """

    default = kwargs.pop('default', _DEFAULT)
    try:
        return min(*args, **kwargs)
    except ValueError:
        if default is _DEFAULT:
            raise
        return default
Milo
  • 335
  • 4
  • 15
0

Considering all the comments above it can be a wrapper like this:

def max_safe(*args, **kwargs):
    """
    Returns max element of an iterable.

    Adds a `default` keyword for any version of python that do not support it
    """
    if sys.version_info < (3, 4):  # `default` supported since 3.4
        if len(args) == 1:
            arg = args[0]
            if 'default' in kwargs:
                default = kwargs.pop('default')
                if not arg:
                    return default

                # https://stackoverflow.com/questions/36157995#comment59954203_36158079
                arg = list(arg)
                if not arg:
                    return default

                # if the `arg` was an iterator, it's exhausted already
                # so use a new list instead
                return max(arg, **kwargs)

    return max(*args, **kwargs)
tsionyx
  • 1,629
  • 1
  • 17
  • 34
  • `max_safe(42, 3, default=0)` Throws `TypeError: max() got an unexpected keyword argument` (python 2) – Milo Sep 19 '19 at 12:36