Безопасная функция max() для пустых списков
Оценка,
max_val = max(a)
приведет к ошибке,
ValueError: max() arg is an empty sequence
Существует ли лучший способ защиты от этой ошибки, отличной от try
, except
catch?
a = []
try:
max_val = max(a)
except ValueError:
max_val = default
Ответы
Ответ 1
В Python 3.4+ вы можете использовать default
аргумент ключевого слова:
>>> max([], default=99)
99
В более низкой версии вы можете использовать or
:
>>> max([] or [99])
99
ПРИМЕЧАНИЕ. Второй подход не работает для всех итераций. особенно для итератора, которые не дают ничего, кроме значения истины.
>>> max(iter([]) or 0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: max() arg is an empty sequence
Ответ 2
В версиях Python старше 3.4 вы можете использовать itertools.chain()
, чтобы добавить другое значение в возможную пустую последовательность. Это будет обрабатывать любые пустые итерации, но обратите внимание, что это не то же самое, что и поставлять аргумент default
, поскольку добавочное значение всегда включено:
>>> from itertools import chain
>>> max(chain([42], []))
42
Но в Python 3.4 по умолчанию игнорируется, если последовательность не пуста:
>>> max([3], default=42)
3
Ответ 3
Другим решением может быть использование троичных операторов:
nums = []
max_val = max(nums) if nums else 0
или
max val = max(iter(nums) if nums else [0])
Ответ 4
_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
Бонус:
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
Ответ 5
Максимум пустой последовательности "должен" быть бесконечно малой величиной любого типа, который имеют элементы последовательности. К сожалению, (1) с пустой последовательностью вы не можете определить, какой тип должны были иметь элементы, и (2) есть, например, не такая вещь, как наибольшее отрицательное целое в Python.
Итак, вам нужно помочь max
, если вы хотите, чтобы в этом случае было что-то разумное. В последних версиях Python есть аргумент default
для max
(который мне кажется вводящим в заблуждение именем, но неважно), который будет использоваться, если вы пройдете в пустой последовательности. В более старых версиях вам просто нужно убедиться, что последовательность, в которой вы проходите, не пуста - например, or
с помощью одноэлементной последовательности, содержащей значение, которое вы хотели бы использовать в этом случае.
[EDITED после публикации, потому что Яков Белх любезно отметил в комментариях, что я написал "бесконечно большой", где я должен был написать "бесконечно мало".]
Ответ 6
Учитывая все комментарии выше, это может быть оболочка, подобная этой:
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/info/36157995#comment59954203_36158079
arg = list(arg)
if not arg:
return default
# if the `arg` was an iterator, it exhausted already
# so use a new list instead
return max(arg, **kwargs)
return max(*args, **kwargs)