简体   繁体   English

为什么 Python 中的 try/except 需要一个 except 块?

[英]Why do we need an except block for the try/except in Python?

I get what it does but I don't understand why we do it?我明白它的作用,但我不明白我们为什么要这样做? Why are we trying to except something?为什么我们要尝试排除某些东西?

For example:例如:

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

Why do I have to do except KeyError?为什么除了 KeyError 我还要做? I don't know why a KeyError is thrown.我不知道为什么会抛出 KeyError 。 Why can't I just do为什么我不能做

result_dict[value] += 1

in the first place?首先?

What goes inside the except block for any try/catch?任何 try/catch 的 except 块里面有什么? I get the error has to be thrown but what condition has to do inside the except block?我得到错误必须被抛出,但是在 except 块内必须做什么条件?

Sorry if my question is too dumb.对不起,如果我的问题太愚蠢了。 I'm a beginner.我是初学者。

Here's why.这就是为什么。 You cant add to something that is None.你不能添加到无的东西。 For example say var1 = None .例如说var1 = None I couldn't do something like var = var + 1 .我不能做类似var = var + 1的事情。 That would throw an error.那会抛出一个错误。 In the example you shared the code will check if you can add 1 to value's key.在您共享的示例中,代码将检查您是否可以将 1 添加到值的键。 If you cannot do this you assign a value to the key.如果您不能这样做,您可以为该键分配一个值。

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

A KeyError will crash/terminate your program if result_dict[value] doesn't exist.如果result_dict[value]不存在, KeyError将崩溃/终止您的程序。 By using an except block, you tell the program what to do in the event of a KeyError (here, assign result_dict[value] = 1 if it doesn't exist), and then the program continues.通过使用except块,您可以告诉程序在发生 KeyError 时要做什么(这里,如果它不存在,则分配result_dict[value] = 1 ),然后程序继续。 Basically, you're avoiding a crash.基本上,您正在避免崩溃。

Let's say your value is "banana" .假设您的value"banana" If you don't have a result_dict["banana"] , you can't add 1 to nothing, ie None += 1 .如果您没有result_dict["banana"] ,则不能将 1 添加到无,即None += 1

By using except KeyError , you intercept the error before it stops your program, and now you have set a key-value pair for your result_dict , rather than terminating the program.通过使用except KeyError ,您可以在错误停止程序之前拦截错误,现在您已经为result_dict设置了一个键值对,而不是终止程序。

TLDR TLDR

try/except blocks makes sure that your program continues running rather than ending abruptly. try/except块确保您的程序继续运行而不是突然结束。 By including a except KeyError , we are specifically making sure that the program does not end abruptly if a KeyError occurs.通过包含一个except KeyError ,我们特别确保在发生KeyError时程序不会突然结束。 result_dict[value] += 1 will throw an error if value is not a key in the dictionary, because it tries to do access a key that does not exist.如果value不是字典中的键, result_dict[value] += 1将抛出错误,因为它试图访问不存在的键。 The += makes the code run similarly to: +=使代码运行类似于:

result_dict[value] = result_dict[value] + 1

and since value is not in result_dict, it is similar to saying并且由于value不在 result_dict 中,因此类似于说

result_dict[value] = None + 1

which is bad.这很糟糕。

The Non-TLDR version非 TLDR 版本

When an error occurs in python, the program usually terminates abruptly and exits.当 python 发生错误时,程序通常会突然终止并退出。 It does not run any code that occurs below the part where the exception occurs.它不会运行发生在异常发生的部分下方的任何代码。 For example, take the following code.例如,采用以下代码。 It takes 2 numbers from the user a and b , and it will output a/b它需要用户ab的 2 个数字,它将 output a/b

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

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

If the user gives a valid input (say a=4, b=2 ) the program proceeds smoothly.如果用户给出了一个有效的输入(比如a=4, b=2 ),程序就会顺利进行。 However if the user were to give, say a = "c" , then the following happens但是,如果用户要给出a = "c" ,那么会发生以下情况

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

And the program ends abruptly.程序突然结束。 It is perfectly valid to have the program end like that, but rarely do you want the program to end abruptly.让程序这样结束是完全有效的,但您很少希望程序突然结束。 Take the case where the user has 1000 inputs, and the last one messes up.以用户有 1000 个输入,而最后一个输入混乱的情况为例。 The user will then have to restart the program and re-input all 1000 inputs again, because the program ended abruptly.然后用户将不得不重新启动程序并再次重新输入所有 1000 个输入,因为程序突然结束。

So now we introduce a try/except block.所以现在我们引入一个try/except块。 We know that converting a non-numeric character into an integer will throw a ValueError as seen in the error, so we will handle them accordingly我们知道将非数字字符转换为 integer 会抛出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)

So now, we take an input from the user, try to convert it into an integer and put that value into a .所以现在,我们从用户那里获取输入,尝试将其转换为 integer 并将该值a . If it succeeds, it will proceed smoothly to the next line ( break ) and exit the loop.如果成功,它将顺利进行到下一行( break )并退出循环。 If it fails, then an exception will occur, and it will enter the except block, where it will print a helpful error message, and run the loop again (until the user enters a valid input).如果失败,则会发生异常,并进入except块,在该块中打印有用的错误消息,并再次运行循环(直到用户输入有效输入)。 Now the program doesn't just terminate abruptly when it fails.现在程序不只是在失败时突然终止。 It gives appropriate feedback to the user, and lets the user retry the input again.它向用户提供适当的反馈,并让用户再次重试输入。

That is a general exception block, where you just catch any exception.那是一个通用异常块,您只需在其中捕获任何异常。

But let's now say that there are multiple possible errors that could occur.但是现在让我们说可能会发生多个可能的错误。 Take the following code:采取以下代码:

a = input()
b = input()

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

Now a few errors can occur.现在可能会出现一些错误。 One of them is the ValueError stated above, where the input given cannot be converted into an integer.其中之一是上述ValueError ,其中给定的输入无法转换为 integer。 The other error, is a ZeroDivisionError where b=0 .另一个错误是ZeroDivisionError其中b=0 Python doesn't like dividing by zero, hence it will throw an exception and terminate the program immediately. Python 不喜欢被零除,因此会抛出异常并立即终止程序。

So now, you want to print a special message for each type of program.所以现在,您想为每种类型的程序打印一条特殊消息。 How you do that is by catching specific exceptions你如何做到这一点是通过捕获特定的异常

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")

If python was unable to convert the input into an integer, it will enter the ValueError block of the code, and print "We cannot convert a non-numeric character to an integer"如果 python 无法将输入转换为 integer,则会进入代码的ValueError块,并打印"We cannot convert a non-numeric character to an integer"

If python tried to divide by 0, then it will enter the ZeroDivisionError block and print "We cannot divide by 0"如果 python 试图除以 0,那么它将进入ZeroDivisionError块并打印"We cannot divide by 0"

If any other type of error occurred, it will end up in the final except block and print "Some unexpected error has occurred"如果发生任何其他类型的错误,它将在最终的 except 块中结束并打印"Some unexpected error has occurred"

And after the exception is handled, it prints "I am done"处理异常后,它会打印"I am done"

Note that if the final except block was not there to catch any other exceptions, then the program will not terminate nicely because the exception was unhandled, and it will not print the final statement.请注意,如果最后的except块没有捕获任何其他异常,那么程序将不会很好地终止,因为该异常未处理,并且它不会打印最后的语句。

Now, how does one realise what possible error can occur?现在,人们如何意识到可能发生的错误? That is up to practice, and once you get familiar with the errors, you can handle them accordingly.这取决于实践,一旦您熟悉了错误,您就可以相应地处理它们。 Or, purposefully make your program throw exceptions, and read the stack trace for what kind of exceptions occur.或者,故意让您的程序抛出异常,并读取堆栈跟踪以了解发生了何种异常。 And then handle them accordingly.然后相应地处理它们。 You don't have to handle each specific error differently, you could handle all of them in the same way.您不必以不同的方式处理每个特定错误,您可以以相同的方式处理所有错误。

You can read more here: Python's documentation for errors and exceptions您可以在此处阅读更多内容: Python 的错误和异常文档

Let's start with an example让我们从一个例子开始

We have tenants in an apartment building who can reserve parking spots.我们在公寓楼里有可以预订停车位的租户。 Some tenants have no car and won't have a parking spot, but only tenants can reserve a spot.一些租户没有车,也没有停车位,但只有租户可以预订停车位。 We track parking spots in a dictionary:我们在字典中跟踪停车位:

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

Chris doesn't have spots because he walks to work.克里斯没有位置,因为他步行上班。

What should happen if Eve tries to reserve a spot?如果 Eve 试图保留一个位置,会发生什么?

parking_spots["eve"]

That code asks "how many spots has eve reserved?"该代码询问“前夕保留了多少个位置?” However, another question this answers is whether Eve is a tenant of the building at all.然而,这个回答的另一个问题是 Eve 是否是该建筑物的租户。 Python represents this by having parking_spots["eve"] throw a KeyError which is not like any other value. Python 通过让parking_spots["eve"]抛出一个不同于任何其他值的KeyError来表示这一点。 If python didn't do this and returned 0 by default, then parking_spots["eve"] and parking_spots["chris"] would look the same.如果 python 没有这样做并且默认返回 0,那么parking_spots["eve"]parking_spots["chris"]看起来是一样的。

But wait, I know that this particular dictionary isn't being used that way但是等等,我知道这本特定的字典不是那样使用的

Cool, that's pretty common.酷,这很常见。 It's so common in fact that there are multiple ways of doing this.事实上,这很常见,有多种方法可以做到这一点。

Instead of代替

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

You can use您可以使用

result_dict = {}
result_dict.setdefault(0)

result_dict += 1

Or you can use defaultdict .或者您可以使用defaultdict

from collections import defaultdict

result_dict = defaultdict(int)
result_dict += 1

Some CS Theory一些CS理论

There are two theoretical concepts that we could talk about here:我们可以在这里讨论两个理论概念:

  • Exceptions as a control flow mechanism异常作为控制流机制
  • The api of a dictionary字典的api

Exceptions for Control Flow控制流的例外

try/catch is style of control flow. try/catch是控制流的风格。 It is simply a nicer way to think of code for certain cases.这只是在某些情况下考虑代码的一种更好的方式。 It is almost never required as long as you can use if/else , but is many times the best way to think about code.只要您可以使用if/else ,几乎从不需要它,但它很多时候是思考代码的最佳方式。

Depending on who you talk to, exception handling can be a somewhat controversial feature: C++ - Arguments for Exceptions over Return Codes根据您与谁交谈,异常处理可能是一个有争议的功能: C++ - Arguments 用于返回代码的异常

In my opinion, it's rare that exceptions are truly the right answer if you design a system from scratch.在我看来,如果您从头开始设计一个系统,那么异常很少是真正正确的答案。 Generally, I prefer using an Option (or more generally sum types).一般来说,我更喜欢使用Option (或更一般地求和类型)。

However, in many cases, they are the best answer given you starting constraints.但是,在许多情况下,考虑到您的起步限制,它们是最佳答案。

Dictionary API字典 API

Python throwing an exception when the key isn't present is a design choice and not the only way the API could be written. Python 在密钥不存在时抛出异常是一种设计选择,并不是编写 API 的唯一方法。 Instead, it could simply return None .相反,它可以简单地返回None Many languages follow the latter approach as they consider it more safe - especially when you work in a modern typed language like Rust, Haskell, Elm, etc.许多语言都遵循后一种方法,因为他们认为它更安全——尤其是当您使用现代类型语言(如 Rust、Haskell、Elm 等)时。

Further Reading延伸阅读

I would also encourage you to read Amp's answer as it covers some other details on the particulars of exception handling that may be instructive.我还鼓励您阅读Amp 的答案,因为它涵盖了可能具有指导意义的异常处理细节的其他一些细节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM