Python Traceback
Python中Traceback的介绍
当代码中发生异常时,Python会返回一个Traceback。如果我们第一次查看它或者不知道它传达了什么信息,那么Traceback的输出可能会让人难以抗拒。然而,在Python编程语言中,Traceback包含了大量的数据,可以帮助我们诊断和修复代码中引发异常的原因。根据Python中的Traceback提供的数据,对于成为一名更好的Python程序员来说至关重要。
在接下来的教程中,我们将讨论Python编程语言中的Traceback。但在教程结束时,我们将能够识别出一些最常见的traceback。
所以,让我们开始吧。
理解Python编程语言中的Traceback
Traceback是一个报告,它记录了在代码的某个特定点调用的函数的行。Traceback被称为多个名称,比如堆栈跟踪、堆栈回溯等等。然而,在Python编程语言中,我们使用术语”Traceback”。
当程序引发异常时,Python返回当前的Traceback,以帮助我们理解出了什么问题。让我们考虑以下示例,说明了这种情况。
示例:
# File name: pytrace.py
# defining a custom function
def welcome( name ):
# printing some message
print( "Hello, " + nam ) # using 'nam' instead of 'name'
print( "Welcome to the Python program!")
# calling the function
welcome( "James" )
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 10, in
welcome( "James" )
File "D:\Python\pytrace.py", line 6, in welcome
print( "Hello, " + nam ) # using 'nam' instead of 'name'
NameError: name 'nam' is not defined
说明:
在上面的代码片段中,我们定义了一个名为 welcome 的自定义函数,该函数接受一个名为 “name” 的参数。然而,在函数内部打印一些消息时,我们将 “name” 参数拼错为 “nam” 。因此,当调用该函数时,Python打印了一个回溯消息。
通过观察输出,我们可以发现回溯消息包含了我们需要诊断问题所需的所有信息。回溯消息的最后一行表达了引发异常的类型,以及与该异常相关的一些适当的数据。回溯消息的早期行指示引发异常的代码。
在上述回溯中,异常是一个 NameError ,这意味着引用了一个尚未定义的名称(如变量、类、函数)。在下面的示例中,名字引用的是 “nam” 。
在上述示例中,最后一行的数据足以帮助我们解决问题。搜索代码中错误拼写的名字 nam ,并正确指示给我们。通常,代码要复杂得多。
在Python中阅读回溯
Python的回溯中包含了关于引发异常的代码行中的宝贵数据。在下面的部分中,我们将了解如何阅读不同的回溯以了解存储在回溯中的不同数据。
Python的回溯被分成不同的部分。每个部分都有其重要性。让我们考虑下面显示的以下回溯:
Traceback:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 10, in
welcome( "James" )
File "D:\Python\pytrace.py", line 6, in welcome
print( "Hello, " + nam ) # using 'nam' instead of 'name'
NameError: name 'nam' is not defined
在Python编程语言中,从下到上阅读回溯消息是一个好的习惯。现在,让我们详细了解上面的回溯:
- 蓝色块: 以蓝色突出显示的最后一行表示错误消息行。此行包含引发异常的名称。
- 绿色块: 在异常名称之后是与错误相关的消息。该消息通常包含有价值的数据,用于理解引发异常的原因。
- 黄色块: 黄色块包含不同的函数调用,从下到上,从最近到最不常见。这些调用使用每个调用的两行条目来表示。每个调用的第一行包含数据,例如文件的名称、行号和模块的名称,所有这些都指示代码的位置。
- 粗体行: 这些粗体行是这些调用的第二行,包含实际处理的代码片段。
当在命令行和REPL中执行代码时,回溯的输出之间存在一些差异。让我们考虑在REPL中执行相同的示例并理解回溯输出。
REPL:
>>> def welcome( name ):
... print( "Hello, " + nam )
... print( "Welcome to the Python program!")
...
>>> welcome( "James" )
Traceback (most recent call last):
File "", line 1, in
File "", line 2, in welcome
NameError: name 'nam' is not defined
正如我们在上面的代码片段中所观察到的,回溯消息返回了” ****”,而不是文件名称,因为我们是通过标准输入输入代码的。此外,回溯消息中不显示已执行的代码行。
注意:如果我们中有些人喜欢在不同的编程语言中查看堆栈跟踪,那么可以发现Python编程语言中回溯的方式与其他语言有很明显的区别。大多数语言将异常返回到顶部,然后从上到下依次显示最近的调用到最早的调用。
而在Python中,应该从下往上阅读回溯。这非常有帮助,因为当回溯返回时,终端通常会停在输出的底部,为我们提供了开始阅读回溯的理想位置。
理解Python中一些常见的回溯
一旦我们理解了在Python中处理异常时如何解读回溯,让我们来了解一些在编程过程中可能遇到的常见回溯。
下面是我们可能遇到的一些标准异常,以及它们的含义、引发原因和回溯中包含的数据。
AttributeError
被称为 AttributeError 的异常是在尝试访问一个对象上没有定义的属性时引发的。Python的文档描述了何时会引发 AttributeError 异常:
当引用或赋值属性失败时,会引发此异常。
让我们来看一个引发 AttributeError 异常的示例。
示例:
# defining a variable
my_int = 10
print(my_int.an_attribute)
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 2, in
print(my_int.an_attribute)
AttributeError: 'int' object has no attribute 'an_attribute'
解释:
在上面的代码片段中,我们定义了一个整数,并尝试访问它的属性。然而,当我们执行程序时,它引发了一个 AttributeError 异常,表示上面的特定对象类型 int 没有被访问的属性,即 an_attribute 。查看错误消息行中的 AttributeError 异常可以方便地帮助我们识别我们尝试访问的属性以及如何修复它。
通常,当出现此类异常时,表示我们可能正在处理的实例不是我们要查找的类型。
让我们考虑另一个示例以更好地说明:
示例:
# defining a list
my_list = ( 10, 20 )
# using the 'append()' method in the list
my_list.append( 30 )
# printing the final list
print( my_list )
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 5, in
my_list.append( 30 )
AttributeError: 'tuple' object has no attribute 'append'
解释:
在上面的代码片段中,我们定义了一个列表并使用 append() 方法向列表中添加了另一个元素。然而,结果我们可能期望 my_list 的类型是 list ,并且包含一个名为 append() 的方法。但是,当我们接收到 AttributeError 异常时,我们观察到它是在调用 append() 函数时引发的,这说明我们可能没有使用我们要查找的对象类型。
通常,当我们希望从方法或函数调用返回的对象是特定类型时,却得到了None类型的对象时,就会发生这种情况。在上述场景中,错误消息行会显示: AttributeError: ‘NoneType’ object has no attribute ‘append’.
ImportError
这个异常也被称为 ImportError ,当 import 语句出现问题时会引发。如果我们尝试导入的模块或库无法找到,或者从库或模块中导入的内容不存在,我们将获得此异常或其子类 ModuleNotFoundError 。Python的文档表示当引发 ImportError 异常时:
当 import 语句难以加载库或模块时,会引发此异常。此外,当 ‘from list’ 在 from … import 中包含无法找到的名称时,也会引发此异常。
让我们考虑一个示例,演示了 ImportError 和 ModuleNotFoundError 是如何引发的。
示例:
# importing a library or module
import xyz
from collections import xyz
输出:
# Output for the first line
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 2, in
import xyz
ModuleNotFoundError: No module named 'xyz'
# Output for the second line
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 3, in
from collections import xyz
ImportError: cannot import name 'xyz' from 'collections' (D:\Python39\lib\collections\__init__.py)
解释:
在上面的代码片段中,我们尝试导入一个不存在的库或模块, xyz ,导致了 ModuleNotFoundError 异常的出现。另一方面,当我们尝试导入不存在的 xyz 模块,但存在的collections库时,程序会抛出 ImportError 异常。错误回溯的底部显示的错误消息行告诉我们哪个具体的东西无法导入,在上述两种情况下,它是 xyz 。
IndexError
该异常也称为 IndexError ,通常在我们尝试从序列或序列中检索索引(如元组或列表)时引发,而该索引在序列或序列中不存在。 Python的文档说明了何时会引发索引异常:
当序列或序列的下标超出范围时,将引发此异常。
让我们考虑以下示例,演示如何引发 IndexError 异常。
示例:
# defining a list
my_list = [ "Apple", "Peaches", "Mango", "Banana" ]
# printing the element of the list
print( my_list[ 4 ] )
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 5, in
print( my_list[ 4 ] )
IndexError: list index out of range
解释:
在上面的代码片段中,我们定义了一个名为 my_list 的列表,其中包含四个元素。然而,当我们尝试打印索引号为 5 的元素时,程序引发了一个 IndexError 异常。错误消息中说明的 IndexError 异常并没有为我们提供正确的信息。我们可以观察到在这种情况下,我们有一个超出范围的序列引用,即 out of range ,再加上序列的类型,即列表。结合其他的回溯信息,这些数据通常足以帮助我们快速识别如何修复问题。
KeyError
这个异常,也被称为 KeyError ,类似于 IndexError 异常,在我们尝试访问一个不存在于映射中的键时引发,通常在像 字典 这样的数据结构中观察到。Python的文档指出了在何种情况下会引发 KeyError 异常:
当字典(映射)中的键未被找到时,会引发此异常。
让我们来看一个示例,了解 KeyError 异常是如何引发的。
示例:
# defining a dictionary
mydict = {'Mike' : 40, 'James' : 25, 'Drake' : 32, 'Jenny' : 28}
# accessing a key out of the dictionary
print( mydict['Sam'] )
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 5, in
print( mydict['Sam'] )
KeyError: 'Sam'
说明:
在上面的代码片段中,我们定义了一个 字典 ,其中为每个 键 分配了一些 值 。 然后,我们尝试访问 键 的 值 ,但在字典中找不到该 键 。结果,程序抛出 KeyError 异常,表示找不到所需的 键 。
NameError
这个异常被称为 NameError ,当我们在代码中引用未定义的变量、类、函数、模块或其他名称时,会引发该异常。Python的文档指出了何时会引发 NameError 异常。
当找不到局部或全局名称时,会引发此异常。
让我们考虑以下示例以了解如何引发 NameError 异常。
示例:
# defining a function
def myself( name ):
print("My name is", nam)
# Calling the function
myself( "Robin" )
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 6, in
myself( "Robin" )
File "D:\Python\pytrace.py", line 3, in myself
print("My name is", nam)
NameError: name 'nam' is not defined
解释:
在上面的示例中,我们定义了一个名为 myself() 的函数,它接受一个名为 name 的参数。然而,在打印一些语句时,我们在下面的一行中错误地将名字拼写为 nam 。然后我们调用了这个函数。结果,程序引发了一个 NameError 异常,因为名字 ‘nam’ 在程序中没有定义。
SyntaxError
这个异常也被称为语法Error ,通常在Python程序的语法不正确时引发。Python的文档表示什么时候会引发语法Error 异常:
这个异常在解析器遇到Python语法错误时引发。
让我们来看一个示例,说明语法Error 异常是如何引发的。
例如:
# defining a function
def myself( name )
print("My name is", nam)
# Calling the function
myself( "Robin" )
输出:
File "D:\Python\pytrace.py", line 2
def myself( name )
^
SyntaxError: invalid syntax
解释:
在上述语法中,我们定义了一个名为 myself() 的函数,但是忘记在函数定义后面加上冒号” : “。结果,当我们执行该函数时,程序引发了语法Error 异常,表示程序语法存在问题。下面代码行下方的^符号表示问题出现的位置。
此外,我们还可以观察到语法Error 的回溯消息不显示正常的第一行,即 “Traceback (most recent call last):” 。这是因为语法Error 异常在Python尝试分析代码行时引发,代码行并没有以逐字方式处理。
TypeError
这个异常也被称为 TypeError ,在语法尝试使用一个无法执行该函数的实例时引发,例如尝试将一个整数与一个字符串相加,或者在未指定长度的对象上调用 len() 函数。Python的文档指出 TypeError 异常被引发的情况:
当将一个函数或操作应用于错误类型的对象时,引发此异常。
让我们来看一个示例,演示了如何引发 TypeError 异常。
示例:
# defining some variables
myint = 10
mystr = '10'
# performing addition on objects of different types
myadd = myint + mystr
# printing the result
print("Result:", myadd)
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 4, in
myadd = myint + mystr
TypeError: unsupported operand type(s) for +: 'int' and 'str'
解释:
在上面的示例中,我们定义了两个变量,一个是 整数 ,一个是 字符串 。然后我们对这些变量执行加法操作,并尝试打印结果。然而,程序返回了 TypeError 异常,因为我们试图将 整数 值与 字符串 值相加。
类似地,当我们在 ‘int’ 数据类型上使用 len() 函数时,也会引发此异常。
让我们考虑下面的示例来说明相同的情况。
示例:
# defining the variable
myint = 10
# finding length of the object of type 'int'
print("Length:", len(myint))
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 5, in
print("Length:", len(myint))
TypeError: object of type 'int' has no len()
解释:
在上面的示例中,我们定义了一个数据类型为 ‘int’ 的变量,并尝试对该变量执行 len() 函数。然而,程序引发了 TypeError 错误,表示我们不能对数据类型为 ‘int’ 的对象执行 len() 函数。
ValueError
当对象的值不正确时,也就是抛出了 ValueError 异常。这个异常与 IndexError 异常类似,因为在 IndexError 异常的情况下,索引值超出了序列的范围。相比之下, ValueError 异常适用于更一般的情况。Python的文档指出了什么时候会引发 ValueError 异常:
每当函数或操作接收到具有正确类型的参数,但其值不适合,并且状态没有被更特定的异常如 IndexError 定义时,就会引发此异常。
让我们考虑一个基于 ValueError 异常的示例。
示例:
# defining the variables
var1, var2, var3 = [10, 20, 30, 40]
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 2, in
var1, var2, var3 = [10, 20, 30, 40]
ValueError: too many values to unpack (expected 3)
解释:
在上面的示例中,我们试图解压四个值,但只得到了三个。因此,程序抛出了 ValueError 异常。
让我们来看一个基于 ValueError 异常的另一个示例。
示例:
# defining the variable
var1, var2, var3, var4 = [10, 20, 30]
输出:
Traceback (most recent call last):
File "D:\Python\pytrace.py", line 2, in
var1, var2, var3, var4 = [10, 20, 30]
ValueError: not enough values to unpack (expected 4, got 3)
说明:
在上面的语法中,我们尝试解包了太多的值。结果,程序返回 ValueError 异常,表示解包的值不够(期望得到4个值,实际只有3个)。