【问题标题】:which style is preferred?哪种风格更受欢迎?
【发布时间】:2010-01-22 02:21:36
【问题描述】:

选项 1:

def f1(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  if c in d:
    return d[c]

  return "N/A"

选项 2:

def f2(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  try:
    return d[c]
  except:
    return "N/A"

这样我就可以打电话了:

for c in ("China", "Japan"):
  for f in (f1, f2):
    print "%s => %s" % (c, f(c))

选项是确定密钥是否事先在目录中(f1),或者只是回退到异常(f2)。哪个是首选?为什么?

【问题讨论】:

  • 除了:是恶业。始终具体说明您有兴趣捕获的内容,在这种情况下为 KeyError
  • 你选了一个糟糕的例子。显而易见的答案不涉及任何风格。

标签: python exception coding-style


【解决方案1】:

我不会去的

def f2(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  return d.get(c, "N/A")

这种方式更短,“get”是专为工作而设计的。

此外,没有显式异常的 except 是不好的做法,因此请使用 except KeyError: 而不仅仅是 except。

异常在 python 中没有太多开销。如果没有更好的选择,或者有时甚至保存属性查找(使用而不是 hasattr),通常最好使用它们。

编辑: 澄清有关异常的一般要点。

paxdiablo 在总体上是正确的。 Python 主要是为“更容易请求宽恕然后许可”而设计的,即尝试然后查看失败(异常),然后“在跳跃之前查看”查看那里的内容然后应用。这是因为 python 中的属性查找可能很昂贵,因此再次调用相同的东西(检查边界)是一种资源浪费。然而,python 内部的东西通常有更好的助手,所以最好使用它们。

【讨论】:

  • 这个问题不是针对dict的,主要是为了展示。我想知道的是事先检查边界或退回到异常。
  • @Dyno:那你应该问那个。一个好的经验法则是“请求宽恕而不是允许”,但我要补充一点,如果常见情况会导致请求宽恕很多,那么你应该先请求宽恕。
  • 你的意思是“就做吧”。如果出了问题,以后再处理?
  • 我会说“去做吧”,但要确保你知道会出什么问题。不要抓住一切,只有你很高兴发生的特定例外。如果你抓住了一切,当出现严重问题时将很难找到,因为你不会得到任何反馈。
【解决方案2】:

一般来说(不一定是 Python),除了最简单的情况外,我倾向于使用“try-then-tell-me-if-it-went-wrong”方法(例外)。这是因为,在线程环境中或在数据库访问期间,底层数据可能会在键检查和值提取之间发生变化。

如果您没有在当前线程之外更改关联数组,那么您可以执行“check-first-then-extract”方法。

但这是针对一般情况的。在这里,具体来说,您可以使用get 方法,该方法允许您在密钥不存在时指定默认值:

return d.get (c, "N/A")

我将澄清我在第一段中所说的内容。在底层数据可能在检查和使用之间发生变化的情况下,您应该始终使用异常类型的操作(除非您有一个不会导致问题的操作,例如上面提到的d.get() )。例如,考虑以下两个线程时间线:

+------------------------------+--------------------+
| Thread1                      | Thread2            |
+------------------------------+--------------------+
| Check is NY exists as a key. |                    |
|                              | Delete NY key/val. |
| Extract value for NY.        |                    |
+------------------------------+--------------------+

当线程 1 尝试提取该值时,无论如何它都会得到一个异常,因此您最好只为这种可能性编写代码并删除初始检查。

关于数据库的评论也很重要,因为这是基础数据可以更改的另一种情况。这就是为什么我更喜欢原子 SQL(如果可能的话)而不是像获取键列表然后用单独的语句处理它们。

【讨论】:

  • 我总是给他们打电话。更容易请求宽恕然后允许”和“在你跳跃之前先看看”。但我同意在 python 中宽恕是你所追求的。
  • 我倾向于将这个和 jweede 都标记为答案,但似乎这是不可行的。
【解决方案3】:

通常,异常会带来一些开销,并且适用于真正的“异常”情况。在这种情况下,这听起来像是执行的正常部分,而不是“异常”或“错误”状态。

一般来说,我认为您的代码将受益于使用“if/else”约定,并仅在真正需要时保存异常。

【讨论】:

  • 这提醒我“按照您的想法编写代码”,“事情按照他们的设计目标做得更好”。所以我把它标记为答案。
  • 当您说异常会带来开销时,您可能是正确的,但 python 样式是 Its better to ask for forgiveness than permission。这意味着你不检查是否有东西。你try 去做,然后请求原谅。如果是我,我会选择try/except
  • 杰夫乔斯说得好。在 python 中尝试并看看有什么用是很聪明的。但是,在这种情况下,当我们不期望有任何异常时使用 try/except 子句并不是一个好主意。就像 Richo 说的:“恶业”。 :-)
【解决方案4】:

都没有。

return d.get(c, 'N/A')

【讨论】:

    【解决方案5】:

    我和大卫一起做这个:

    def f2(c):
       d = {
            "USA": "N.Y.",
            "China": "Shanghai"
           }
    
       return d.get(c, "N/A")
    

    ...这正是我要写的。

    解决您的其他选择:

    在 'f1()' 中,这本身并没有什么问题,但字典有一个 get() 方法几乎可以用于这个确切的用例:“从字典中获取这个,如果它不存在,则返回这个取而代之的是其他东西”。这就是你的代码所说的,使用 get() 更简洁。

    在 'f2()' 中,像这样单独使用 'except' 是不受欢迎的,此外,您实际上并没有做任何有用的事情来响应异常——在您的情况下,调用代码永远不会知道有一个例外。那么,如果它不能为您的函数或调用它的代码增加价值,为什么还要使用该构造呢?

    【讨论】:

      【解决方案6】:

      我看到人们使用“get”,这是我推荐的。但是,如果您将来发现自己处于类似情况,请捕获您的意思的异常:

      try:
          return d[k]
      except KeyError:
          return "N/A"
      

      这样,其他异常(包括KeyboardInterrupt)就不会被捕获。你几乎永远不想赶上KeyboardInterrupt

      【讨论】:

        【解决方案7】:

        我同意在这种情况下,dict.get 是最好的解决方案。

        一般来说,我认为您的选择将取决于例外情况的可能性有多大。如果您希望密钥查找大部分通过,那么 try/catch 是 IMO 更好的选择。同样,如果它们经常失败,则使用 if 语句会更好。

        异常与属性查找的性能在 Python 中并没有太大区别,所以我更担心使用异常/look-before-you-leap 的逻辑而不是性能方面。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-09-18
          • 2023-03-29
          • 2018-02-25
          • 2011-03-08
          • 2016-01-18
          • 1970-01-01
          • 2021-02-20
          • 2019-06-11
          相关资源
          最近更新 更多