微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么我们需要在 Python 中的 try/except 块中使用一个 except 块? TLDR非 TLDR 版本

如何解决为什么我们需要在 Python 中的 try/except 块中使用一个 except 块? TLDR非 TLDR 版本

我明白它的作用,但我不明白我们为什么要这样做?为什么我们要尝试排除某些东西?

例如:

    for i,value in enumerate(arr):
        try:
            result_dict[value] += 1
        except KeyError:
            result_dict[value] = 1

除了 KeyError 之外,为什么我必须这样做?我不知道为什么会抛出 KeyError 。为什么我做不到

result_dict[value] += 1

首先?

任何 try/catch 的 except 块里面有什么?我知道必须抛出错误,但在 except 块内必须执行什么条件?

对不起,如果我的问题太愚蠢了。我是初学者。

解决方法

这就是原因。你不能添加到没有的东西。例如说var1 = None。我不能做类似 var = var + 1 的事情。那会抛出错误。在您共享的示例中,代码将检查您是否可以将 1 添加到值的键。如果您不能这样做,您可以为键分配一个值。

for i,value in enumerate(arr):
        try:
            #Try and add 1 to a dict key's value. 
            result_dict[value] += 1
        except KeyError:
            #Say we get an error we need to assign the dict key's value to 1.
            result_dict[value] = 1
,

如果 KeyError 不存在,result_dict[value] 将崩溃/终止您的程序。通过使用 except 块,您可以告诉程序在发生 KeyError 时该怎么做(这里,如果它不存在,则分配 result_dict[value] = 1),然后程序继续。基本上,您是在避免崩溃。

假设您的 value"banana"。如果您没有 result_dict["banana"],则不能将 1 添加到空值中,即 None += 1

通过使用 except KeyError,您可以在程序停止之前拦截错误,现在您已经为 result_dict 设置了一个键值对,而不是终止程序。

,

TLDR

try/except 块确保您的程序继续运行而不是突然结束。通过包含 except KeyError,我们特别确保在发生 KeyError 时程序不会突然结束。如果 result_dict[value] += 1 不是字典中的键,value 将抛出错误,因为它试图访问不存在的键。 += 使代码运行类似于:

result_dict[value] = result_dict[value] + 1

并且由于 value 不在 result_dict 中,因此类似于说

result_dict[value] = None + 1

哪个不好。

非 TLDR 版本

当python出现错误时,程序通常会突然终止并退出。它不会运行发生在发生异常的部分下方的任何代码。例如,采用以下代码。它从用户 ab 获取 2 个数字,它会输出 a/b

a = int(input("Enter a value for a: "))
b = int(input("Enter a value for b: "))

print("a/b is:",a/b)

如果用户提供了有效的输入(比如 a=4,b=2),程序将顺利进行。但是,如果用户要给予,例如 a = "c",则会发生以下情况

Traceback (most recent call last):
  File "<string>",line 1,in <module>
ValueError: invalid literal for int() with base 10: 'c'

程序突然结束。让程序像这样结束是完全有效的,但您很少希望程序突然结束。以用户有 1000 个输入的情况为例,最后一个搞砸了。然后用户将不得不重新启动程序并重新输入所有 1000 个输入,因为程序突然结束。

所以现在我们引入一个 try/except 块。我们知道将非数字字符转换为整数会抛出 ValueError,如错误所示,因此我们将相应地处理它们

while True:
    try:
        a = int(input("Enter a value for a: "))
        break
    except:
        print("An error has occurred. Please input a again")

while True:
    try:
        b = int(input("Enter a value for b: "))
        break
    except:
        print("An error has occurred. Please input b again")

print("a/b is:",a/b)

现在,我们接受用户的输入,尝试将其转换为整数并将该值放入 a。如果成功,它将顺利进行到下一行(break)并退出循环。如果失败,则会发生异常,并进入except块,在那里打印有用的错误消息,并再次运行循环(直到用户输入有效的输入)。现在,程序不会在失败时突然终止。它向用户提供适当的反馈,并让用户重新尝试输入。

这是一个通用的异常块,您可以在其中捕获任何异常。

但现在假设可能发生多种可能的错误。取以下代码:

a = input()
b = input()

print(int(a)/int(b))
print("I am done")

现在可能会出现一些错误。其中之一是上面提到的 ValueError,其中给定的输入不能转换为整数。另一个错误是 ZeroDivisionError 其中 b=0。 Python 不喜欢除以零,因此它会抛出异常并立即终止程序。

现在,您想为每种类型的程序打印一条特殊消息。如何做到这一点是通过捕获特定异常

a = input("Enter a value for a: ")
b = input("Enter a value for b: ")

try:
    print(int(a)/int(b))
except ValueError:
    print("We cannot convert a non-numeric character to an integer")
except ZeroDivisionError:
    print("We cannot divide by 0")
except:
    print("Some unexpected error has occurred")

print("I am done")

如果python无法将输入转换为整数,它会进入代码的ValueError块,并打印"We cannot convert a non-numeric character to an integer"

如果python试图除以0,那么它会进入ZeroDivisionError块并打印"We cannot divide by 0"

如果发生任何其他类型的错误,它将在最终的 except 块中结束并打印 "Some unexpected error has occurred"

并且在异常处理后,打印"I am done"

请注意,如果最后一个 except 块没有用于捕获任何其他异常,那么程序将不会很好地终止,因为异常未得到处理,并且不会打印最终语句。

现在,人们如何意识到可能发生的错误?这取决于实践,一旦您熟悉了错误,您就可以相应地处理它们。或者,故意让您的程序抛出异常,并读取堆栈跟踪以了解发生了哪种异常。然后相应地处理它们。您不必以不同方式处理每个特定错误,您可以以相同方式处理所有错误。

您可以在此处阅读更多信息:Python's documentation for errors and exceptions

,

让我们从一个例子开始

我们有一栋公寓楼的租户可以预订停车位。有些租户没有车,也没有停车位,但只有租户可以预订位置。我们在字典中跟踪停车位:

parking_spots = { "alice": 1,"bob": 2,"chris": 0 }

Chris 没有空位,因为他走路上班。

如果 Eve 试图预订位置会怎样?

parking_spots["eve"]

该代码询问“eve 预留了多少个位置?”然而,这回答的另一个问题是夏娃是否是这栋建筑的租户。 Python 通过让 parking_spots["eve"] 抛出一个与任何其他值不同的 KeyError 来表示这一点。如果 python 没有这样做并且默认返回 0,那么 parking_spots["eve"]parking_spots["chris"] 看起来是一样的。

但是等等,我知道这本特定的词典并没有那样使用

很酷,这很常见。事实上,这很常见,有多种方法可以做到这一点。

代替

result_dict = {}
for i,value in enumerate(arr):
    try:
        result_dict[value] += 1
    except KeyError:
        result_dict[value] = 1

你可以使用

result_dict = {}
result_dict.setdefault(0)

result_dict += 1

或者您可以使用 defaultdict

from collections import defaultdict

result_dict = defaultdict(int)
result_dict += 1

一些CS理论

我们可以在这里讨论两个理论概念:

  • 异常作为控制流机制
  • 字典的api

控制流异常

try/catch 是控制流的样式。在某些情况下,这只是一种更好的代码思考方式。只要您可以使用 if/else,它几​​乎从不必需,但很多时候它是思考代码的最佳方式。

根据您与谁交谈,异常处理可能是一个有点争议的功能: C++ - Arguments for Exceptions over Return Codes

在我看来,如果您从头开始设计系统,那么例外是真正正确的答案是很少见的。通常,我更喜欢使用 Option(或更一般的和类型)。

但是,在许多情况下,如果您有起始限制,它们是最好的答案。

字典API

当密钥不存在时,Python 抛出异常是一种设计选择,而不是编写 API 的唯一方式。相反,它可以简单地返回 None。许多语言都采用后一种方法,因为他们认为它更安全 - 特别是当您使用 Rust、Haskell、Elm 等现代类型语言时。

进一步阅读

我还鼓励您阅读Amp's answer,因为它涵盖了一些可能具有指导意义的异常处理细节的其他细节。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。