问题描述
我明白它的作用,但我不明白我们为什么要这样做?为什么我们要尝试排除某些东西?
例如:
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出现错误时,程序通常会突然终止并退出。它不会运行发生在发生异常的部分下方的任何代码。例如,采用以下代码。它从用户 a
和 b
获取 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,因为它涵盖了一些可能具有指导意义的异常处理细节的其他细节。