一文教你读懂 Python 中的异常信息

640?wx_fmt=jpeg

正文共:11813 字 2 图

预计阅读时间: 30 分钟

原文:https://realpython.com/python-traceback/

译者:陈祥安 原文有所改动。

在写 Python 代码的时候,当代码中出现错误,会在输出的时候打印 Traceback  错误信息,很多初学者看到那一堆错误信息,往往都会处于懵逼状态,脑中总会冒出一句,这都是些啥玩意。如果你是第一次看到它,也许你不知道它在告诉你什么。虽然 Python 的 Traceback  提示信息看着挺复杂,但是里面丰富的信息,可以帮助你诊断和修复代码中引发异常的原因,以及定位到具体哪个文件的哪行代码出现的错误,所以说学会看懂 Traceback  信息是非常重要的,另外在面试的时候也经常会问到 Python 中的异常类型及其含义,那么,接下来就让我们对其进行详细理解。

什么是 Traceback

Traceback 是 Python  错误信息的报告。在其他编程语言中有着不同的叫法包括 stack trace, stack  traceback, backtrac  等名称, 在 Python  中,我们使用的术语是 Traceback。后面我提到的错误信息等词都表示Traceback。

# example.py 
def  greet(someone ):
    print('Hello, ' + someon )

greet('Chad')

这里首先定义了函数 greet,然后传入参数 someone,然后函数内,一个 print  语句其中 someon  是一个没有定义的变量,

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  5, in  <module>
    greet ('Chad')
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  3, in  greet 
    print ('Hello, ' + someon )
NameError: name  'someon' is  not  defined 

此错误输出包含诊断问题所需的所有信息。错误输出的最后一行一般会告诉你引发了什么类型的异常,以及关于该异常的一些相关信息。错误信息的前几行指出了引发异常的代码文件以及行数。

如何阅读 Python 的 Traceback  信息?

当你想确定代码为什么引发异常的时侯,可以根据 Python  的 Traceback  获取许多有用的信息。下面,将列举一些常见的 Traceback,以便理解 Tracebac 中包含的不同信息。

Python Traceback 信息一览

每个 Python 的 Traceback  信息都有几个重要的部分。下图显示了各个组成部分:

640?wx_fmt=jpeg

  • 蓝框:Traceback 的最后一行为错误消息行。其中包含引发的异常名称。

  • 绿框:异常名称后面是错误消息。此消息通常包含有用的信息,用于了解引发异常的原因。

  • 黄色方框:阅读顺序由下而上,最下面的信息,是抛出错误的最外层的位置,越往上代码调用深度越深。

  • 红色下划线:第二行就是实际执行的代码语句了。

一个具体的?

通过一些特定的 Traceback 信息,可以帮助我们更好地理解并查看 Traceback 将提供什么信息。

def  who_to_greet(person ):
    return  person  if  person  else  input ('Greet  who? ')

def  greet(someone, greeting='Hello'):
    print(greeting  + ', ' + who_to_greet (someone ))

def  greet_many(people):
    for  person  in  people:
        try:
            greet(person )
        except  Exception:
            print ('hi, ' + person )

定义一个 who_to_greet  函数,然后接受一个值 person,并根据 if  判断返回相应结果。

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/greetings.py", line  17, in  <module>
    greet ('chad',greting  ='Yo')
TypeError: greet () got  an  unexpected  keyword  argument  'greting'

之前我们说过阅读 Python 的 Traceback  信息,是由下而上进行阅读的,这里我们再一起看一看。

# example.py 
from  greetings  import  greet 
greet (1)

运行之后的结果

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  3, in  <module>
    greet (1)
  File  "/Users/chenxiangan/pythonproject/demo/greetings.py", line  6, in  greet 
    print (greeting  + ', ' + who_to_greet (someone ))
TypeError: can  only  concatenate  str  (not  "int") to  str 

在本例中引发的异常同样是一个类型错误,但这一次消息的帮助要小一些。它只是告诉你,在代码的某个地方,字符串只能和字符串拼接,不能是 int。,而是正在执行的函数的名称 greet()。
然后继续往上看,一行执行的代码,我们看到问题代码是 greet()函数调用时传入了一个整数。
有时在引发异常之后,另一部分代码会捕获该异常并导致异常。在这种情况下,Python 将按接收顺序输出所有异常信息,最外层的异常信息处于 Traceback 内容的最下面位置。
可能看起来有点懵,下面使用一个具体例子进行说明。
在 greetings.py  文件中调用 greet_many  方式具体调用代码如下:

greet_many (['Chad', 'Dan', 1])

运行之后输出的错误信息如下

Hello, Chad 
Hello, Dan 
Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/greetings.py", line  12, in  greet_many 
    greet (person )
  File  "/Users/chenxiangan/pythonproject/demo/greetings.py", line  6, in  greet 
    print (greeting  + ', ' + who_to_greet (someone ))
TypeError: can  only  concatenate  str  (not  "int") to  str 

During  handling  of  the  above  exception, another  exception  occurred:

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/greetings.py", line  17, in  <module>
    greet_many (['Chad', 'Dan', 1])
  File  "/Users/chenxiangan/pythonproject/demo/greetings.py", line  14, in  greet_many 
    print ('hi, ' + person )
TypeError: can  only  concatenate  str  (not  "int") to  str 

emmmmm,这次好像不太一样,比之前的内容多了不少,而且有两个 Traceback 块信息,这是什么意思呢?

During  handling  of  the  above  exception, another  exception  occurred:

它的意思是:在处理上述异常期间,发生了另一个异常。简单理解就是在 except 中的代码出现了异常。所以导致了这种现象。

Python 中有哪些常见的异常类型

在编程时,知道如何在程序引发异常时读取 Python 异常信息非常有用,如果再了解一些常见的异常类型那就更好了。

AttributeError

当你访问一个对象的属性,但是这个属性并没有在这个对象定义的时候,就会引发 AttributeError。

a  = 1
a.b 

运行之后引发异常

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  2, in  <module>
    a.b 
AttributeError: 'int' object  has  no  attribute  'b'

AttributeError 的错误消息行告诉我们特定对象类型(在本例中为 int)没有访问的属性,

大多数情况下,引发这个异常表明你正在处理的对象可能不是你期望的类型。

a_list  = (1, 2)
a_list.append (3)

运行之后抛出异常信息

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  2, in  <module>
    a_list.append (3)
AttributeError: 'tuple' object  has  no  attribute  'append'

这里尝试给 a_list 对象进行 append 操作但是引发了异常,

a_list  = None 
a_list.append (3)

运行抛出异常

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  2, in  <module>
    a_list.append (3)
AttributeError: 'NoneType' object  has  no  attribute  'append'

是不是很眼熟啊,遇到这种情况不要慌,分析看看你的哪个对象是 None  就好了。

ImportError

在使用 import 导入模块时,如果要导入的模块找不到,或者从模块中导入模块中不存在的内容。这时就会触发 ImportError 类型的错误或者它的子类 ModuleNotFoundError。

import  aaa 

运行后输出

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  1, in  <module>
    import  aaa 
ModuleNotFoundError: No  module  named  'aaa'

在这个例子中可以看到,当我们使用 import 导入一个不存在的模块时,就会出现 ModuleNotFoundError 的错误,Traceback 最下面一句信息给出了原因,

from  collections  import  asdf 

运行之后的内容

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  1, in  <module>
    from  collections  import  asdf 
ImportError: cannot  import  name  'asdf' from  'collections'

根据前面的经验我们可以得知原因,不能从 collections 模块中导入名为 asdf 的模块。uvloop这两个包,但是在 unix 系统上是可以运行的,这个时候我们就可以使用下面的方法。

try:
    import  ujson  as  json 
except  ImportError  as  e:
    import  json 

首先导入 ujson 然后使用 as  给他重命名为 json,如果出现错误就会进入 except 模块

IndexError

当你尝试从序列(如列表或元组)中检索索引,但是序列中找不到该索引。此时就会引发 IndexError。

a_list  = ['a', 'b']
a_list[3]

运行之后的结果

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  2, in  <module>
    a_list[3]
IndexError: list  index  out  of  range 

通过 IndexError 的错误消息的最后一不能得到一个准确的信息,只知道一个超出范围的序列引用以及序列的类型,在本例中是一个列表。我们需要往上阅读错误信息,才能确定错误的具体位置。这里我们得知错误代码是 a_list[3]原因是索引3 超出了列表的范围,因为最大就是1(索引下标从0 开始的)。

KeyError

与 IndexError 类似,当你访问映射(通常是 dict )中不包含的键时,就会引发 KeyError。

a_dict={}
a_dict['b']

运行之后

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  2, in  <module>
    a_dict['b']
KeyError: 'b'

KeyError  的错误消息行给出找不到关键字 b。并没有太多的内容,但是,结合上面的错误信息,就可以解决这个问题。

NameError

当你引用了变量、模块、类、函数或代码中没有定义的其他名称时,将引发 NameError。

def  greet (person ):
    print (f'Hello, {persn}')
greet ('World')

运行之后

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  3, in  <module>
    greet ('World')
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  2, in  greet 
    print (f'Hello, {persn}')
NameError: name  'persn' is  not  defined 

NameError  traceback 的错误消息行给出了缺失的名称 persn。

SyntaxError

当代码中有不正确的 Python 语法时,就会引发 SyntaxError。

def  greet (person )

运行之后

  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  1
    def  greet (person )
                    ^
SyntaxError: invalid  syntax 

SyntaxError 的错误消息行只告诉你代码的语法有问题。查看上面的行才能得到问题所在的行,通常会用一个^(插入符号)指向问题点。

TypeError

当你的代码试图对一个无法执行此操作的对象执行某些操作时,例如将字符串添加到整数中,以及一开始的例子使用 append 方法给元组添加元素,这些都会引发 TypeError。

>>> 1 + '1'
Traceback  (most  recent  call  last ):
  File  "<stdin>", line  1, in  <module>
TypeError: unsupported  operand  type (s ) for  +: 'int' and  'str'
>>> '1' + 1
Traceback  (most  recent  call  last ):
  File  "<stdin>", line  1, in  <module>
TypeError: must  be  str, not  int 
>>> len (1)
Traceback  (most  recent  call  last ):
  File  "<stdin>", line  1, in  <module>
TypeError: object  of  type  'int' has  no  len ()

以上所有引发类型错误的示例都会产生包含不同消息的错误消息行。它们每一个都能很好地告诉你哪里出了问题。

  • 第一个是尝试在 int 中拼接一个 str。

  • 第二个是尝试在 str 中拼接一个 int。

错误消息行反映了这些差异。

ValueError

当对象的值不正确时就会引发 ValueError。这个和我们前面说的因为索引的值不在序列的范围内,而导致 IndexError 异常类似。

>>> a, b, c  = [1, 2]
Traceback  (most  recent  call  last ):
  File  "<stdin>", line  1, in  <module>
ValueError: not  enough  values  to  unpack  (expected  3, got  2)
>>> a, b  = [1, 2, 3]
Traceback  (most  recent  call  last ):
  File  "<stdin>", line  1, in  <module>
ValueError: too  many  values  to  unpack  (expected  2)

这些示例中的 ValueError 错误消息行可以准确地告诉我们值的一些问题:

如何记录这些错误信息呢?

前面我们说了很多异常的相关知识,但是我们应该如何利用好呢,这里我们就重点说一下,如何通过记录异常信息,方便后期程序的调试。

import  requests 
url  = "http://wwww.baidu.com"
response  = requests.get (url )

print (response.status_code, response.text )

这是一个访问百度的例子,运行之后,我们成功获取了他的状态码和网页源码。

import  requests 
url  = "http://urlis 233.com"
response  = requests.get (url )

print (response.status_code, response.text )

运行之后我们发现程序出现了错误,下面分析下这些错误信息

省略前面部分
During  handling  of  the  above  exception, another  exception  occurred:

Traceback  (most  recent  call  last ):
  File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  3, in  <module>
    response  = requests.get (url )
  File  "/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/api.py", line  75, in  get 
    return  request ('get', url, params=params, **kwargs )
  File  "/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/api.py", line  60, in  request 
    return  session.request (method=method, url=url, **kwargs )
  File  "/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/sessions.py", line  533, in  request 
    resp  = self.send (prep, **send_kwargs )
  File  "/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/sessions.py", line  646, in  send 
    r  = adapter.send (request, **kwargs )
  File  "/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/adapters.py", line  516, in  send 
    raise  ConnectionError (e, request=request )
requests.exceptions.ConnectionError: HTTPConnectionPool (host='urlis 233.com', port=80): Max  retries  exceeded  with  url: / (Caused  by  NewConnectionError ('<urllib 3.connection.HTTPConnection  object  at  0x 10faeba 90>: Failed  to  establish  a  new  connection: [Errno  8] nodename  nor  servname  provided, or  not  known'))

这个错误信息很长,它引发了许多其他的异常,最终的异常类型是 requests.exceptions.ConnectionError。

File  "/Users/chenxiangan/pythonproject/demo/exmpale.py", line  3, in  <module>
    response  = requests.get (url )

进而定位到错误,这个错误原因主要是不存在地址"http://urlis 233.com",所以访问失败。

错误我们清楚了,但是一大堆的错误信息搭载控制台上,这样看很不美观,而且因为异常的原因我们的程序中断了。这个时候我们就可以使用 Python  中的异常处理模块 try/except 将代码改成下面这样

import  requests 
url  = "http://urlis 233.com"
try:
   response  = requests.get (url )
except  requests.exceptions.ConnectionError:
    print ("-1","链接有问题,访问失败")
else:
    print (response.status_code, response.text )

再次运行可以得到下面的结果

-1 链接有问题,访问失败

ok,我们的程序可以正常运行了,输出的信息也美观了。

你可以通过导入 logging 模块,记录这些错误,最终代码如下

import  logging 
import  requests 

logger  = logging.getLogger (__name__)
url  = "http://urlis 233.com"

try:
    response  = requests.get (url )
except  requests.exceptions.ConnectionError  as  e:
    logger.exception ()
    print (-1, '链接有问题,访问失败')
else:
    print (response.status_code, response.content )

现在,当你再运行有问题的 URL 的脚本时,不仅会打印错误,同时还会在日志文件中记录这些错误信息。过于日志的其他信息可以看我之前的文章。

logging模块配置笔记

总结

Python 的 Traceback 包含很多的信息,它可以帮助你找到 Python 代码中出现的问题。这些错误信息可能看起来有点令人生畏,但是一旦你分解它然后去理解,你就会发现它们非常的有用。

推荐阅读

Python 爬虫面试题 170 道:2019 版

pdb调试神器使用终极指南

添加微信[italocxa].回复:加群,加入Python交流群

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值