【问题标题】:Pythonic way to assign default values分配默认值的 Pythonic 方式
【发布时间】:2011-11-30 10:42:55
【问题描述】:

考虑这一行:

some_value = lst.attr[idx]

这里有两个可能的错误,attr 可能不存在,idx 可能超出范围。

有什么优雅的方法可以减少这种说法吗?理想情况下,是这样的:

some_value = lst.attr[idx] or default_value

(不要在家里尝试。这只适用于正确定义的计算结果的表达式。)

当然可以:

try:
    some_value = lst.attr[idx]
except:
    some_value = default_value

但是,如果我处于作业的上下文中怎么办?例如:

print [x.attr[idx] for x in y]

在这种情况下,处理错误和分配默认值的 Python 方法是什么?

【问题讨论】:

  • 这不是很常见的任务,所以没有(我想)内置解决方案。但是你可以编写自己的 getter 函数并在任何你想要的地方使用它(也可以在列表推导中)。
  • exception 有什么问题?这 Pythonic。 “如果我在赋值的上下文中怎么办”显示的是打印语句,而不是赋值。您能否澄清声明以及except 可能导致的问题?目前尚不清楚为什么except 是不可接受的。请澄清。
  • @S.Lott - 列表理解是赋值的一个例子。想想 if-else 块和三元运算符之间的区别。
  • “列表理解是一个赋值的例子”?不是它不是。没有分配任何东西。这很令人困惑。您能否解释一下通过在print 语句周围添加标准except 子句会产生什么确切 问题。
  • 假设列表推导计算为[1,2,3]。如果2 无法评估,我希望我的列表为[1,'x',3](或其他),并且应该打印。根据您的建议,如果 [1,2,3] 失败,我唯一可以打印的是 error somewhere

标签: error-handling python


【解决方案1】:

您需要决定您要在这里实现的目标。使用“错误”一词可能具有误导性。

如果您实际上是在尝试处理将错误类型的对象传递给您的函数的情况,那么您不想处理这种情况并应该引发异常。

如果您试图让您的函数用于一系列不同的类型,那么这并不是真正的错误,使用默认值可能是合理的。

最简单的选择是先测试属性是否存在。例如:

if hasattr(lst, "attr"):
    attr = lst.attr
else:
    attr = {}

我假设lst.attr 是一个字典,在这种情况下,您可以像这样处理默认值:

lst.attr.get(idx, default_value)

永远不要使用try/except 语句,其中未指定要捕获的异常。你最终可能会掩盖比你想要的更多的东西。

对于您的最后一段代码,我认为您不应该尝试在一行中解决它。可读性很重要。我对下面的代码不满意,但如果将 xyattr 替换为更具描述性的名称,它会得到改进。

attrs = [(x.attr if hasattr(x) else {}) for x in y]

print [attr.get(idx, default_value) for attr in attrs]

【讨论】:

  • 我不会使用hasattr - 它在后台使用try/except。只需将if 语句替换为try: 并将else: 替换为except AttributeError:。当然符合 EAFP 理念。
【解决方案2】:

在这种情况下,pythonic 处理错误和分配默认值的方法是什么?

>>> import this
...
Explicit is better than implicit.
...
Sparse is better than dense.
Readability counts.
...
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.

我觉得 "Pythonic 分配默认值的方法"* 要么处理异常——正如你在问题中已经提到的那样——要么写给你自己的吸气剂。

【讨论】:

    【解决方案3】:

    编写一个函数并让它足够聪明:

    def get_attr_with_index_and_default(obj, attr_name, index, default):
        try:
            return getattr(obj, attr_name)[index]
        except (AttributeError, IndexError):
             return default
    
    print [get_attr_with_index_and_default(x, 'attr', idx, some_default) for x in y]
    

    如果你控制x的类,你可以把它作为一个方法使用,或者把它改编成一个描述符,但我认为这是不值得的,并且会导致代码晦涩难懂和难以追踪的错误。

    【讨论】:

      【解决方案4】:

      即使存在单线,也确实很复杂。例如。这个仍然不能处理索引问题:

      some_value = getattr(lst, 'attr', {idx: default_value})[idx]
      

      我建议为您的lst 写一些getter。

      【讨论】:

        【解决方案5】:

        当对象中有'attr'时

        class C(object):
            attr = "attr on class"
        lst = C()
        print lst.attr if hasattr(lst,'attr') else "default value"
        

        当没有属性'attr'时

        class C(object):
            #attr = "attr on class"
            pass
        lst = C()
        print lst.attr if hasattr(lst,'attr') else "default value"
        

        【讨论】:

          【解决方案6】:

          没有简单、优雅的解决方案可以解决您的问题。例如,如果必须使用可用于列表理解的单行来完成,那么您可以执行以下操作:

          # If lst.attr is a dict.
          some_value = getattr(lst, 'attr', {}).get(idx, default_value)
          # OR
          some_value = lst.attr.get(idx, default_value) if hasattr(lst, 'attr') else default_value
          # OR
          some_value = lst.attr[idx] if hasattr(lst, 'attr') and idx in lst.attr else default_value
          
          # If lst.attr is a sequence.
          some_value = lst.attr[idx] if idx < len(getattr(lst, 'attr', ())) else default_value
          # OR
          some_value = lst.attr[idx] if hasattr(lst, 'attr') and idx < len(lst.attr) else default_value
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-03-18
            • 1970-01-01
            • 2011-04-15
            • 2013-02-25
            • 1970-01-01
            • 1970-01-01
            • 2022-01-04
            • 2015-02-16
            相关资源
            最近更新 更多