【问题标题】:A safe max() function for empty lists空列表的安全 max() 函数
【发布时间】:2016-03-22 15:04:44
【问题描述】:

评估,

max_val = max(a)

会导致错误,

ValueError: max() arg is an empty sequence

除了tryexcept 捕获之外,还有更好的方法来防范此错误吗?

a = []
try:
    max_val = max(a)
except ValueError:
    max_val = default 

【问题讨论】:

标签: python list exception error-handling max


【解决方案1】:

在 Python 3.4+ 中,您可以使用default keyword argument

>>> 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

【讨论】:

  • 3.4 之前的另一个选项是创建一个模仿新行为的my_max 函数。
  • max(iter([]) or 0) 正是让我首先感到悲痛的原因......您对如何处理它有任何进一步的建议吗?其次,我从来没有想过要检查文档......我认为这样一个简单的功能是一个一进一出的过程
  • @AlexanderMcFarlane,邓肯提出了一个不错的选择。看看吧。
  • @AlexanderMcFarlane,我不是母语人士。我不明白one-in-one-out process 是什么。这是否意味着一项共同的任务?
  • @falsetru by one-in-one-out 我的意思是一个参数输入和一个返回值:)
【解决方案2】:

在 3.4 之前的 Python 版本中,您可以使用 itertools.chain() 将另一个值添加到可能为空的序列中。这将处理任何空的可迭代对象,但请注意,它与提供 default 参数并不完全相同,因为始终包含额外的值:

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

但在 Python 3.4 中,如果序列不为空,则忽略默认值:

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

【讨论】:

  • 是否有任何理由使用chain 而不是简单的列表连接,例如max( [42], [] ) 默认是42?
  • @MajorInc 用多个参数调用max 与用单个参数调用它的含义不同。使用单个可迭代参数max 将返回它包含的最大元素,但使用多个参数它会直接比较它们(即它不会迭代)。例如max([42], []) 是一个列表 [42]max(chain([42], []))max([], default=42) 都给出 42
【解决方案3】:

一个空序列的最大值“应该”是一个无限小的东西,无论序列的元素有什么类型。不幸的是,(1)对于一个空序列,您无法判断元素应该具有什么类型,并且(2)没有诸如 Python 中最负整数之类的东西。

因此,如果您希望它在这种情况下做一些明智的事情,您需要帮助 max。在最新版本的 Python 中,max 有一个 default 参数(在我看来这是一个误导性的名称,但没关系),如果您传入一个空序列,将使用该参数。在旧版本中,您只需确保传入的序列不为空 - 例如,通过 oring 使用包含您希望在这种情况下使用的值的单例序列。

[在发布后很长时间后进行了编辑,因为 Yaakov Belch 在 cmets 中好心地指出我写了“无限大”,而我应该写“无限小”。]

【讨论】:

  • 这违反直觉:空列表的最大值应该无限小(因此 max(x,list...)=max(x,max(list...)))和空列表的最小值应该无限大。
  • 哎呀,是的,我写错了;我很抱歉。我会修正我的答案并感谢您发现错误。 [... 完毕。再次感谢。]
【解决方案4】:

另一种解决方案是使用三元运算符:

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

max val = max(iter(nums) if nums else [0])

【讨论】:

    【解决方案5】:
    _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
    

    【讨论】:

      【解决方案6】:

      可以创建简单的 lambda 来执行此操作:

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

      你可以这样称呼它:
      get_max(your_list)

      【讨论】:

        【解决方案7】:

        考虑到它上面的所有 cmets 可以是这样的包装器:

        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)
        

        【讨论】:

        • max_safe(42, 3, default=0) 抛出 TypeError: max() got an unexpected keyword argument (python 2)
        猜你喜欢
        • 2015-04-01
        • 2021-10-01
        • 2022-12-30
        • 2022-01-23
        • 2023-03-14
        • 1970-01-01
        • 2021-09-21
        • 2021-09-11
        • 1970-01-01
        相关资源
        最近更新 更多