您的位置:

Python 中的回溯

Python 中的回溯技术简介

当代码中出现异常时,Python 会返回一个回溯。如果我们第一次看到追溯的输出,或者不知道它向我们传达了什么信息,它会非常不可抗拒。然而,Python 编程语言中的回溯有大量数据可以支持我们诊断和修复代码中引发异常的原因。为了成为更好的 Python 程序员,依赖于 Python 中的回溯提供的数据是至关重要的。

在下面的教程中,我们将讨论 Python 编程语言中的回溯。但是在教程的最后,我们将能够识别一些最常见的回溯。

那么,让我们开始吧。

理解 Python 编程语言中的回溯

一个回溯是一个报告,在特定点的代码行中有对函数的调用。回溯通过多个名称来标识,例如栈跟踪、栈回溯、回溯、等等。然而,我们在 Python 编程语言中使用术语“回溯”。

每当程序引发异常时,Python 都会返回当前的回溯,以帮助我们确认哪里出错了。让我们考虑下面的例子来说明一个这样的场景。

示例:


# 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”。但是,在打印功能内的一些消息时,我们将“名称”参数拼错为“Nam”。因此,当调用函数时引发异常时,Python 会打印一条回溯消息。

正如我们在输出中看到的,回溯消息包含了诊断问题所需的所有信息。追溯消息的最后一行表示引发的异常类型,以及与该异常相关的一些适当数据。追溯消息的前几行表示导致引发异常的代码。

在上面的追溯中,异常是名称错误,,这意味着对某个尚未定义的名称(如变量、类、函数)的引用。在以下情况下,该名称指的是“南”。

上述案例中的最后一行有足够的数据来帮助我们解决问题。搜索名称 nam 的代码,拼写错误,将正确指示我们。一般来说,代码要复杂得多。

阅读 Python 中的回溯

Python 中的回溯有很多关于代码行中引发的异常的有价值的数据。在下一节中,我们将了解如何读取不同的回溯,以便确认存储在回溯中的不同数据位。

Python 回溯分为不同的部分。每个部分都有其重要性。让我们考虑如下所示的回溯:

回溯:


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 编程语言中,从下到上阅读回溯消息是一种很好的做法。现在,让我们详细了解上面的追溯:

  1. 蓝色块:最后一行用蓝色突出显示表示错误信息行。这一行由引发异常的名称组成。
  2. 绿色块:异常名称后是与错误相关的消息。该消息通常包含有价值的数据,以了解引发异常的原因。
  3. 黄色块:黄色块包含从下到上移动的函数的不同调用,从最近到最近。这些呼叫用每个呼叫的两行条目来表示。每个调用的第一行由文件名称、行号和模块名称等数据组成,所有这些数据都指示代码可以在哪里找到。
  4. 粗线:这些粗线是这些调用的第二行,由处理过的代码的实际片段组成。

在命令行中执行代码时,回溯的输出与 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 <module>File "<stdin>", line 2, in welcome
NameError: name 'nam' is not defined</stdin></module> 

正如我们在上面的 REPL 代码片段中所观察到的,回溯消息在文件名的位置返回“ ”,因为我们已经通过标准输入键入了代码。此外,执行的代码行没有显示在追溯消息中。

注意:如果我们中的一些人喜欢在不同的编程语言中查看栈跟踪,那么在 Python 编程语言中用于比较的回溯方式是一个非常明显的区别。大多数语言在顶部返回异常,然后从顶部到底部,最近的调用到最近的调用。

而在 Python 中,回溯应该从下往上读。这非常有帮助,因为当回溯被返回时,终端通常结束于输出的底部,为我们提供了开始读取回溯的最佳位置。

理解 Python 中的一些常见回溯

一旦我们理解了如何在 Python 中每当出现异常时读取回溯,让我们了解一些在编码时可以看到的常见回溯。

以下是我们可能会遇到的一些标准异常,以及它们的含义、引发它们的原因,以及我们可以在它们的回溯中找到的数据。

属性错误

试图访问没有定义属性的对象上的属性时,会引发名为属性错误的异常。Python 的文档描述了何时引发属性错误异常:

当属性的引用或赋值失败时,会引发此异常。

让我们考虑下面的例子,其中属性错误异常已经被引发。

示例:


# 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' 

说明:

在上面的代码片段中,我们定义了一个整数,并尝试访问它的属性。然而,当我们执行程序时,它引发了一个属性错误异常,表示特定对象类型 int 在上述情况下没有访问的属性,即 an_attribute 。查看错误消息行中的属性错误异常可以方便地帮助我们识别我们试图访问的属性以及如何修复它。

一般来说,每当引发类似这样的异常时,这意味着我们可能正在处理的实例不是我们要寻找的类型。

让我们考虑另一个更好的例子:

示例:


# 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() 的方法。当我们收到属性错误异常时,我们观察到它是在调用 append() 函数时引发的,该函数向我们表示我们可能没有使用我们正在寻找的对象类型。

一般来说,这种情况发生在我们寻找从方法或函数调用返回的特定类型的对象时,但最终,我们得到的是无类型对象。在上面的场景中,错误的消息行将显示,AttributeError:“NoneType”对象没有属性“append”。

导入错误

异常,也称为 ImportError ,每当有东西超出了 import 语句的界限时就会引发。如果我们试图导入的模块或库找不到,或者从库中导入的东西碰巧不在其中,我们将获得这个异常,或者它的子类被称为 ModuleNotFoundError 。Python 的文档表示当出现导入错误或异常时:

每当 import 语句发现很难加载库或模块时,就会引发此异常。此外,每当中的【来自名单】从...导入包含无法定位的名称。

让我们考虑一个例子,演示导入错误和模块错误是如何产生的。

示例:


# 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 <module>from collections import xyz
ImportError: cannot import name 'xyz' from 'collections' (D:\Python39\lib\collections\__init__.py)</module> 

说明:

在上面的代码片段中,我们尝试导入了一个不存在的库或模块, xyz ,导致了 ModuleNotFoundError 异常的情况。另一方面,当我们试图从存在的收藏库中导入不存在的模块 xyz 时,程序引发了 ImportError 异常。追溯底部的错误消息行向我们显示了哪个特定的东西不能被导入,并且在上述两种情况下,它都是 xyz 。

索引错误

异常,也称为 IndexError ,通常在我们试图从序列或序列(如元组或列表)中检索索引时引发,并且在序列或序列中找不到该索引。Python 的文档表示何时引发索引异常:

每当序列或序列的下标超出范围时,就会引发此异常。

让我们考虑以下示例,演示如何引发索引错误异常。

示例:


# 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 的元素时,程序引发了索引错误异常。索引错误异常的错误消息没有为我们提供正确的知识。我们可以观察到,我们有一个序列引用,即超出范围除了序列的类型之外,在下面的场景中有一个列表。连同其余的追溯,这些数据通常足以帮助我们快速识别如何修复问题。

键错误

该异常也称为键错误,类似于索引错误异常,每当我们试图访问不在映射中的键时都会引发该异常,通常在数据结构中观察到,如字典。Python 的文档表示当出现键错误异常时:

每当在现有密钥集中找不到字典(映射)密钥时,就会引发此异常。

让我们考虑下面的例子来理解键错误异常是如何产生的。

示例:


# 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' 

说明:

在上面的代码片段中,我们定义了一个字典,其中一些键和值被分配给每个键。然后我们尝试访问键的值,该值在字典中不可用。结果,程序抛出键错误异常,表示找不到我们要找的键。

名称错误

每当我们引用了尚未在代码行中定义的变量、类、函数、模块或其他名称时,就会引发异常,也称为名称错误。Python 的文档表示何时出现名称错误异常。

每当找不到本地或全局名称时,就会引发此异常。

让我们考虑以下示例,以了解如何引发名称错误异常。

示例:


# 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 

说明:

在上面的例子中,我们定义了一个函数为本人(),它以一个参数作为的名字。然而,我们在打印一些语句时,在下面一行中拼错了名称 nam 。然后我们调用了函数。因此,程序引发了名称错误异常,因为程序中没有定义名称‘Nam’。

句法误差

异常,也称为语法错误,通常在 Python 程序的语法不正确时引发。Python 文档表示当出现语法错误异常时:

每当解析器遇到 Python 语法错误时,就会引发此异常。

让我们考虑一个示例,说明如何引发语法错误异常。

示例:


# 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

说明:

在上面的语法中,我们已经自己定义了一个函数为(),但是忘记在函数定义后面加上冒号“ : ”标记。因此,当我们执行该函数时,程序引发了语法错误异常,表示程序的语法有问题。代码行下方的^(插入符号)标记表示问题的位置。

此外,我们可以观察到语法错误回溯消息没有显示常规的第一行语句“回溯(最近一次调用最后):”。这是因为当 Python 试图分析代码行时,会引发语法错误异常,并且这些代码行不是以文字方式处理的。

类型错误

每当语法试图用无法执行某个函数的实例来执行某个函数时,就会引发异常,该异常也称为 TypeError ,例如试图向字符串添加一个整数,或者在未指定长度的对象上调用 len() 函数。Python 文档表示当出现类型错误异常时:

每当对错误类型的对象应用函数或操作时,都会引发此异常。

让我们考虑以下示例,演示如何引发类型错误异常。

示例:


# 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' 

说明:

在上例中,我们将两个变量定义为一个整数和一个字符串。然后,我们对这些变量执行加法运算,并尝试打印结果。然而,程序返回了类型错误异常,因为我们试图将整数值与字符串值相加。

同样,当我们在 '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() 函数。然而,程序引发了类型错误,称我们无法对数据类型为‘int’的对象执行 len() 功能。

值错误

每当对象的值不正确时,就会引发异常,也称为值错误。该异常类似于索引错误异常,因为在索引错误异常的情况下,索引值超出了序列的范围。相比之下,值错误异常是针对更一般的情况。Python 的文档表示当出现值错误异常时:

每当函数或操作接收到正确类型的参数时,就会引发此异常;然而,未设置的值和状态不是由更具体的异常定义的,如索引错误。

让我们考虑一个基于值错误异常的例子。

示例:


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

说明:

在上面的例子中,我们尝试解包四个值,但只得到三个。因此,程序引发了值错误异常。

让我们考虑另一个基于值错误异常的例子。

示例:


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

说明:

在上面的语法中,我们试图解包太多的值。结果,程序返回值错误异常,表示没有足够的值来解包(应为 4,得到 3)。