跳转到内容

Python 入门教程/异常处理

来自维基教科书,开放的书籍,为开放的世界

如果你以前没见过它们,那你还不够努力。它们是什么?错误。异常。问题。明白我的意思吗?我的这个程序就遇到了这种问题

代码示例 1 - 有错误的程序
def menu(list, question):
    for entry in list:
        print 1 + list.index(entry),
        print ") " + entry

    return raw_input(question) - 1

# running the function
# remember what the backslash does
answer = menu(['A','B','C','D','E','F','H','I'],\
'Which letter is your favourite? ')

print 'You picked answer ' + (answer + 1)

这只是我们之前做的菜单程序的一个例子。在我看来,它看起来很完美。至少在我第一次尝试运行它之前是这样的。运行程序,会发生什么?

错误 - 人为错误

[编辑 | 编辑源代码]

你代码中最常见的问题都是你自己造成的。很遗憾,但事实如此。当我们尝试运行我们残疾的程序时,我们看到了什么?

代码示例 2 - 错误信息
Traceback (most recent call last):
  File "/home/steven/errortest.py", line 10, in -toplevel-
    answer = menu(< I'll snip it here >)
  File "/home/steven/errortest.py", line 6, in menu
    return raw_input(question) - 1
TypeError: unsupported operand type(s) for -: 'str' and 'int'

这是什么意思?Python 试图告诉你(但很难找到一个合适的词)你不能将一个字母字符串和一个数字连接成一个文本字符串。让我们看一下错误信息,看看它如何告诉我们这一点。

  • File "/home/steven/errortest.py", line 10, in -toplevel- 告诉我们一些信息。File "/home/steven/errortest.py" 告诉我们错误发生在哪个文件中。如果你使用了很多相互引用的模块,这将很有用。line 10, in -toplevel- 告诉我们错误发生在文件中的第 10 行,并且发生在顶层(也就是说,没有缩进)。
  • answer = menu(['A','B','C','D','E','F','H','I'],'Which letter is your favourite? ') 重复了错误发生的代码。
  • 由于这行代码调用了一个函数,接下来的两行描述了错误发生在函数中的哪个位置。
  • TypeError: unsupported operand type(s) for -: 'str' and 'int' 告诉你错误类型。在本例中,它是一个 "TypeError",表示你尝试对不兼容的变量进行减法运算。

对于一个错误,会有多个文件和代码列表,因为错误是由于两行代码的交互而发生的(例如,当使用函数时,错误发生在调用函数的那一行,以及函数中出错的那一行)。

现在我们知道了问题是什么,如何解决它?错误信息已经隔离了问题所在,所以我们只关注那段代码。

代码示例 3 - 调用 menu 函数
answer = menu(['A','B','C','D','E','F','H','I'],\
'Which letter is your favourite? ')

这是一个函数调用。错误发生在函数中的以下一行代码中

代码示例 4 - 错误发生的地方
return raw_input(question) - 1

raw_input 始终返回一个字符串,因此出现了我们的问题。让我们将其更改为 input(),当你输入一个数字时,它会返回一个数字。

代码示例 5 - 修复错误
return input(question) - 1

错误修复!

异常 - 代码的局限性

[编辑 | 编辑源代码]

好的,当你在正常情况下执行程序时,它能正常工作。但是,如果你尝试一些奇怪的事情呢?输入一个字母(比如 "m")而不是一个数字?哎哟!

代码示例 6 - 另一个错误信息
Traceback (most recent call last):
  File "/home/steven/errortest.py", line 10, in -toplevel-
    answer = menu(< I'll snip it here >)
  File "/home/steven/errortest.py", line 6, in menu
    return input(question) - 1
  File "", line 0, in -toplevel-
NameError: name 'm' is not defined

这告诉我们什么?有两行代码列表 - 一行在第 10 行,另一行在第 6 行。它告诉我们,当我们在第 10 行调用 menu 函数时,在第 6 行(我们减去 1 的地方)发生了错误。如果你知道 input() 函数的作用,这就会说得通 - 我阅读了一些资料并进行了测试,发现如果你输入一个字母或单词,它会认为你在提及一个变量!所以,在第 6 行,我们试图从变量 "m" 中减去 1,而 "m" 不存在。

你不知道如何修复它?最好的也是最简单的方法之一是使用 try 和 except 操作符。

这是一个在程序中使用 try 的示例。

代码示例 7 - try 操作符
try:
    function(world, parameters)
except:
    print world.errormsg

这是一个我试图修复的非常混乱的代码段的示例。首先,运行 try: 下面的代码。如果发生错误,编译器会跳到 except 部分并打印 world.errormsg。程序不会停止并崩溃,它会运行 except: 下面的代码,然后继续执行。

让我们在代码中发生错误的地方(第 6 行)试试看。现在 menu 函数是

代码示例 8 - 测试我们的修复
def menu(list, question):
    for entry in list:
        print 1 + list.index(entry),
        print ") " + entry
    try:
        return input(question) - 1
    except NameError:
        print "Enter a correct number"

当你被要求输入一个数字时,尝试输入一个字母,看看会发生什么。糟糕。我们修复了一个问题,但现在它导致了另一个问题。这种情况经常发生。(有时你会陷入循环,因为你的代码太乱了)。让我们看看这个错误。

代码示例 9 - 又一个错误信息
Traceback (most recent call last):
  File "/home/steven/errortest.py", line 12, in -toplevel-
    print 'You picked answer', (answer + 1)
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

这次发生了什么?menu 函数没有返回值 - 它只打印了一个错误信息。当我们在程序结束时尝试打印返回值加 1 时,返回值是什么?没有返回值?那么 1 加...,嗯,我们不知道我们要把 1 加到什么东西上!

我们只需返回任何数字,但这相当于撒谎。我们真正应该做的是重写程序,让它能够处理这个异常。用什么处理?用 try 和 except!

代码示例 10 - 另一个解决方案
# from when we finish defining the function
answer = menu(['A','B','C','D','E','F','H','I'],\
'Which letter is your favourite? ')
try:
    print 'You picked answer', (answer + 1)
    # you can put stuff after a comma in the 'print' statement,
    # and it will continue as if you had typed in 'print' again
except:
    print '\nincorrect answer.'
    # the '\n' is for formatting reasons. Try without it and see.

问题再次解决。

无休止的错误

[编辑 | 编辑源代码]

我们上面使用的这种方法并不推荐。为什么?因为除了我们知道可能发生的错误外,except: 还捕获了所有其他错误。如果这意味着我们永远看不到可能导致后续问题发生的错误怎么办?如果 except: 捕获了所有可能的错误,我们就无法控制要处理哪些错误,以及我们想要看到的其他错误,因为到目前为止我们还没有处理它们。我们也几乎无法在一个代码块中处理多种类型的错误。当一切都无望时,该怎么办?以下是一个遇到这种情况的代码示例。

代码示例 11 - 我们面临的问题
print 'Subtraction program, v0.0.1 (beta)'
a = input('Enter a number to subtract from > ')
b = input('Enter the number to subtract > ')
print a - b

好的,你输入了两个数字,它能正常工作。输入一个字母,它会给你一个 NameError。让我们重写代码,只处理 NameError。我们将把程序放在一个循环中,这样如果发生错误,它就会重新启动(使用 continue,它会从顶部重新开始循环,使用 break,它会退出循环)。

代码示例 12 - 处理 NameError
print 'Subtraction program, v0.0.2 (beta)'
loop = 1
while loop == 1:
    try:
        a = input('Enter a number to subtract from > ')
        b = input('Enter the number to subtract > ')
    except NameError:
        print "\nYou cannot subtract a letter"
        continue
    print a - b
    try:
        loop = input('Press 1 to try again > ')
    except NameError:
        loop = 0

在这里,如果输入错误,我们将重新启动循环。在第 12 行,我们假设你想要退出程序,如果你没有按 1,所以我们退出了程序。

但是仍然存在问题。如果我们留空或输入一个不寻常的字符,例如 ! 或 ;,程序会给我们一个 SyntaxError。让我们处理这个问题。当我们要求输入要减去的数字时,我们会给出不同的错误信息。当我们要求按 1 时,我们还会假设用户想要退出。

代码示例 13 - 现在,处理 SyntaxError
print 'Subtraction program, v0.0.3 (beta)'
loop = 1
while loop == 1:
    try:
        a = input('Enter a number to subtract from > ')
        b = input('Enter the number to subtract > ')
    except NameError:
        print "\nYou cannot subtract a letter"
	continue
    except SyntaxError:
        print "\nPlease enter a number only."
	continue
    print a - b
    try:
        loop = input('Press 1 to try again > ')
    except (NameError,SyntaxError):
        loop = 0

如你所见,你可以使用多个 except,每个 except 处理一个不同的问题。你也可以使用一个 except 来处理多个异常,方法是将它们放在括号内并用逗号隔开。

现在我们有一个程序,它很难被最终用户崩溃。作为最后的挑战,看看你是否能使其崩溃。我已经想到了一种方法 - 如果你仔细阅读了关于人为错误的部分,你可能知道它是什么。

华夏公益教科书