【问题标题】:Is creating a variable in try/except block considered a bad practice?在 try/except 块中创建变量是否被认为是一种不好的做法?
【发布时间】:2018-01-25 17:26:38
【问题描述】:

让我们考虑这个例子 - 代码返回列表 li(如果存在)中第一个 None 值的索引,或者如果 None 不存在于 li 中,则返回最后一个元素的索引。

我可以检查 None 是否在列表中,但使用 ifs 不是首选方式 - Dive Into Python 鼓励使用异常,因为据说它们更快并且使代码更清晰(请参阅this 问题)。所以我提出了三种方法:

1.

try:
    my_index = li.index(None)
except ValueError:
    my_index = len(li) - 1

print my_index

my_index 在 try/except 块中声明。更少的行,没有多余的声明。可能的问题 - 不同的异常类型将导致无法创建 my_index 并导致 print 上的脚本崩溃。

2.

my_index = None  # or anything else
try:
    my_index = li.index(None)
except ValueError:
    my_index = len(li) - 1

print my_index

my_index 在 try/except 之前声明,并且无论发生什么异常都会分配一个值。缺点 - 更多的行,看起来多余。

3. 编辑:不起作用 - finally 执行,无论 try/except 结果如何

try:
    my_index = li.index(None)
except ValueError:
    my_index = len(li) - 1
finally:
    my_index = None  # or anything else

print my_index

优点 - 总是有效(或至少我认为如此)。缺点 - 在我的整个 Python 体验中,我从未见过有人使用 finally,这一定是有原因的。

哪些方法被认为是首选和 Pythonic 方法?也许有更好的解决方案?

【问题讨论】:

  • 我可能会使用 1.。你说:“不同的异常类型将导致不创建 my_index”,但如果发生这种情况,那么代码逻辑就会出现严重错误:li 甚至不存在,或者它是一个没有 @987654335 的对象@ 方法,所以你可能想要在那个时候崩溃(或有一些其他错误处理),你可能不想愉快地继续使用并不真正相关的默认my_index

标签: python exception-handling try-catch


【解决方案1】:
try:
    my_index = li.index(None)
except ValueError:
    my_index = len(li) - 1

print my_index

可能的问题 - 不同的异常类型将导致无法创建 my_index 并导致 print 上的脚本崩溃。

呃,不。如果引发任何其他异常,则整个代码块将被中止,因为异常冒泡到调用者。因此,如果引发除ValueError 之外的任何其他异常类型,print 将永远不会被执行。这是一个非常理智的行为:您处理您期望的错误并且 可以 处理,并让其他错误导致整个块中止,可能是整个脚本。这是您想要的最明智和最简洁的解决方案。

【讨论】:

  • 好点,我没有测试不同的异常类型,我错过了一个事实,即其他(未处理的)异常将在调用者处停止执行(因此print 将不会执行)。谢谢!
【解决方案2】:

第三个选项不起作用。查看finally 上的文档 (强调我的):

finally 子句总是在离开 try 语句之前执行,无论是否发生异常。

所以在第三个选项中,无论发生什么,my_index 总是无。

这给我们留下了第一个和第二个选项。值得注意的是,第二个是您将在大多数静态类型语言中看到的。然而,Python 更宽松一些,并且允许任何一种选择。

我认为您关于不是ValueError 的异常的论点是无效的。另一个异常将停止执行并返回给调用者,因此永远不会到达打印。所以第一个解决方案应该可以正常工作。

【讨论】:

  • except Exception: raise...?这真是太多余了。外部try 块表示“如果引发任何异常,则引发异常,否则照常继续。” 这意味着整个外部try 块是多余的。
  • @deceze 再看一遍我意识到完全可以将print 移动到try 子句中,此时我们不妨完全摆脱它。所以,是的,多余的。
【解决方案3】:

++ 正如@deceze 已经注意到的那样,除了 ValueError: 将只捕获此类的错误,而不会捕获其他错误。

如果确实需要对不同的错误情况进行不同的处理,可以提供更多的异常子句:

try:
    ...
except ValueError:
    ...
except TypeError:
    ...

还支持except中的序列:

try:
    ...
except (ValueError, TypeError, ...):
    ...

+++ 如果通过了空列表,您应该考虑您的代码需要返回什么:-1?引发异常?

【讨论】:

  • 谢谢!它有效,但阅读起来非常不清楚。不过,不同方法的优点。
  • 道歉。我已经认为这种方法很糟糕并删除了代码) Index() 确实更快,尽管在更复杂的情况下您可能会发现生成器表达式很有用。
  • 当然。感谢序列,从未使用过。
【解决方案4】:

我认为这是一个你应该使用异常的例子。虽然 Python 使用异常作为“常规”编程结构来降低复杂性,但我认为这是增加复杂性的一个案例。您的代码告诉我 my_index 必须在这些行之后保存一个值,并且您希望 li 是可迭代的,即使发生异常也是如此。这个异常是ValueError,这是None 不在列表中的结果,这是一个额外的复杂性,并且只能按照惯例理解 - 此外,您确定它不是可以抛出的IndexError 吗?你看,恕我​​直言构造

if None in li: my_index = li.index(None)
else         : my_index = len(li) - 1

不会让半年内遇到这个代码的程序员,也就是你自己,在理解你的程序逻辑方面偏离轨道,而出现异常的版本会导致额外的脑力劳动,如果你的预期会引发问题可能的异常和反应是完整和正确的。只有当这是一段对性能非常关键的代码时,您才应该尝试避免双重搜索。

【讨论】:

  • 在我看来,您的第一个代码示例与 try/except 方法一样具有可读性(尽管我宁愿坚持使用 pep8 格式),但第二种方法方式更少比 try/except 更易读。回答您的问题 - 这不是性能关键,可读性是关键。
  • 第二种方法有问题,抱歉。但是,很抱歉,对于程序员来说,这不应该是不可读的。
  • @deceze 是的,但是如果您在这里阅读了答案和 cmets,他们会在异常构造的正确性周围圈出大约 80%,所以我对这个保持我的看法。
猜你喜欢
  • 2015-10-29
  • 2015-07-20
  • 2017-12-18
  • 2022-12-22
  • 2017-07-28
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
相关资源
最近更新 更多