一文教你读懂Python中的异常信息
<p><img src="https://img2018.cnblogs.com/blog/736399/201908/736399-20190813223719948-1406350643.jpg" alt="" loading="lazy"></p><p>正文共:11813 字 2 图<br>
预计阅读时间: 30 分钟<br>
原文:https://realpython.com/python-traceback/<br>
译者:陈祥安 原文有所改动。</p>
<p>在写 Python代码的时候,当代码中出现错误,会在输出的时候打印 Traceback错误信息,很多初学者看到那一堆错误信息,往往都会处于懵逼状态,脑中总会冒出一句,这都是些啥玩意。如果你是第一次看到它,也许你不知道它在告诉你什么。虽然 Python的 Traceback提示信息看着挺复杂,但是里面丰富的信息,可以帮助你诊断和修复代码中引发异常的原因,以及定位到具体哪个文件的哪行代码出现的错误,所以说学会看懂 Traceback信息是非常重要的,那么,接下来就让我们对其进行详细理解。</p>
<h3 id="什么是-traceback">什么是 Traceback</h3>
<p>Traceback是 Python错误信息的报告,其中包含在特定点的代码中进行的函数调用。 在其他编程语言中有着不同的叫法包括 stacktrace, stacktraceback, backtrac等名称, 在 Python中,我们使用的术语是 Traceback。<br>
当你的程序导致异常时,Python将打印 Traceback以帮助你知道哪里出错了。下面是一个例子来说明这种情况</p>
<pre><code># example.py
defgreet (someone ):
print ('Hello, ' + someon )
greet ('Chad')
</code></pre>
<p>这里首先定义了函数 greet,然后传入参数 someone,然后函数内,一个 print语句其中 someon是一个没有定义的变量,<br>
然后通过 greet ('Chad'),调用刚才定义的 greet函数,运行之后会出现如下错误信息。<br>
(Python中的错误信息开头就是 Traceback。)</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line5, in<module>
greet ('Chad')
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line3, ingreet
print ('Hello, ' + someon )
NameError: name'someon' isnotdefined
</code></pre>
<p>此错误输出包含诊断问题所需的所有信息。错误输出的最后一行一般会告诉你引发了什么类型的异常,以及关于该异常的一些相关信息。错误信息的前几行指出了引发异常的代码文件以及行数。<br>
在上面的错误信息中,异常类型是 NameError,意思是名称使用了一个没定义的名称(变量、函数、类)的引用。在本例中,引用的名称是 someon。<br>
一般情况下看错误信息的最后一行就能定位到错误的原因。然后在代码中搜索错误提示中的名称"someon",然后发现这是一个拼写错误,然后我们改成 someone即可。<br>
然而,有些代码的错误信息要比这个复杂的多。</p>
<h3 id="如何阅读-python--的-traceback--信息">如何阅读 Python的 Traceback信息?</h3>
<p>当你想确定代码为什么引发异常的时侯,可以根据 Python的 Traceback获取许多有用的信息。下面,将列举一些常见的 Traceback,以便理解 Tracebac中包含的不同信息。</p>
<h4 id="python--traceback--信息一览">PythonTraceback信息一览</h4>
<p>每个 Python的 Traceback信息都有几个重要的部分。下图显示了各个组成部分:<br>
<img src="https://img2018.cnblogs.com/blog/736399/201908/736399-20190813223748198-1081444506.jpg" alt="" loading="lazy"><br>
在 Python中,最好从下到上阅读错误信息:</p>
<ul>
<li>蓝框:Traceback的最后一行为错误消息行。 其中包含引发的异常名称。</li>
<li>绿框:异常名称后面是错误消息。 此消息通常包含有用的信息,用于了解引发异常的原因。</li>
<li>黄色方框:阅读顺序由下而上,最下面的信息,是抛出错误的最外层的位置,越往上代码调用深度越深。<br>
然后每个出错的文件会有两条错误信息,第一行是 File后面紧跟着文件的路径,然后是行数,最后是模块或者方法名。<br>
在 Pycharm中点击文件的链接即可定位到错误的位置。</li>
<li>红色下划线:第二行就是实际执行的代码语句了。</li>
</ul>
<h4 id="一个具体的">一个具体的🌰</h4>
<p>通过一些特定的 Traceback信息,可以帮助我们更好地理解并查看 Traceback将提供什么信息。<br>
通过下面的示例代码来说明 Python中 Traceback所提供的信息</p>
<pre><code>defwho_to_greet (person ):
returnpersonifpersonelseinput ('Greetwho? ')
defgreet (someone, greeting='Hello'):
print (greeting+ ', ' + who_to_greet (someone ))
defgreet_many (people ):
forpersoninpeople:
try:
greet (person )
exceptException:
print ('hi, ' + person )
</code></pre>
<p>定义一个 who_to_greet函数,然后接受一个值 person,并根据 if判断返回相应结果。<br>
然后,greet函数接受一个 someone和一个可选的 greeting,之后调用 print函数,在 print中调用 who_to_greet函数并传入参数 someone。<br>
最后,greet_many (),将迭代 people列表并调用 greet函数。 如果通过调用 greet ()引发异常,则会打印一个简单的问候语。<br>
只要提供了正确的输入,此代码就没有任何可能导致异常被引发的错误。<br>
如果你在 greetings.py中调用 greet函数,并传入值(例如 greet ('chad',greting='Yo')),那么你将获得以下 Traceback信息</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/greetings.py", line17, in<module>
greet ('chad',greting='Yo')
TypeError: greet () gotanunexpectedkeywordargument'greting'
</code></pre>
<p>之前我们说过阅读 Python的 Traceback信息,是由下而上进行阅读的,这里我们再一起看一看。<br>
首先,我们需要看的是错误信息的最后一行,通过最后一行可以知道错误的类型以及一些错误原因。<br>
意思是说:调用 greet ()的时候使用了一个未知的参数,这个未知参数就是 greting。<br>
好的,然后我们需要继续向上看,可以看到导致异常的行。 在这个例子中我们看到的是调用 greet方法的具体代码。<br>
它的上一行提供了代码所在文件的路径,以及代码文件的行号以及它所在的模块。(Pycharm中通过点击文件链接可以定位到具体位置)<br>
在这个例子中,因为我们的代码没有使用任何其他 Python模块,所以我们在这里看到<module>,它表示所处位置是在执行的文件。<br>
使用不同的文件和不同的调用方式调用 greet方法,得到的 Traceback信息也是不同的,下面就通过文件导入的形式来执行 greet方法。看看结果有什么区别吧</p>
<pre><code># example.py
fromgreetingsimportgreet
greet (1)
</code></pre>
<p>运行之后的结果</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line3, in<module>
greet (1)
File"/Users/chenxiangan/pythonproject/demo/greetings.py", line6, ingreet
print (greeting+ ', ' + who_to_greet (someone ))
TypeError: canonlyconcatenatestr(not"int") tostr
</code></pre>
<p>在本例中引发的异常同样是一个类型错误,但这一次消息的帮助要小一些。它只是告诉你,在代码的某个地方,字符串只能和字符串拼接,不能是 int。<br>
向上移动,可以看到执行的代码行。然后是文件和行号的代码。不过,这一次我们得到的不是<module>,而是正在执行的函数的名称 greet ()。<br>
然后继续往上看,一行执行的代码,我们看到问题代码是 greet ()函数调用时传入了一个整数。<br>
有时在引发异常之后,另一部分代码会捕获该异常并导致异常。 在这种情况下,Python将按接收顺序输出所有异常信息,最外层的异常信息处于 Traceback内容的最下面位置。<br>
可能看起来有点懵,下面使用一个具体例子进行说明。<br>
在 greetings.py文件中调用 greet_many方式具体调用代码如下:</module></p>
<pre><code>greet_many (['Chad', 'Dan', 1])
</code></pre>
<p>运行之后输出的错误信息如下</p>
<pre><code>Hello, Chad
Hello, Dan
Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/greetings.py", line12, ingreet_many
greet (person )
File"/Users/chenxiangan/pythonproject/demo/greetings.py", line6, ingreet
print (greeting+ ', ' + who_to_greet (someone ))
TypeError: canonlyconcatenatestr(not"int") tostr
Duringhandlingoftheaboveexception, anotherexceptionoccurred:
Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/greetings.py", line17, in<module>
greet_many (['Chad', 'Dan', 1])
File"/Users/chenxiangan/pythonproject/demo/greetings.py", line14, ingreet_many
print ('hi, ' + person )
TypeError: canonlyconcatenatestr(not"int") tostr
</code></pre>
<p>emmmmm,这次好像不太一样,比之前的内容多了不少,而且有两个 Traceback块信息,这是什么意思呢?<br>
注意这句话</p>
<pre><code>Duringhandlingoftheaboveexception, anotherexceptionoccurred:
</code></pre>
<p>它的意思是:在处理上述异常期间,发生了另一个异常。简单理解就是在 except中的代码出现了异常。所以导致了这种现象。<br>
这个例子就是在第三次循环的时候 person=1 然后字符串 hi和1 不能进行拼接操作,然后再次引发了异常。<br>
查看所有的错误信息输出可以帮助您了解异常的真正原因。<br>
有时,当您看到最后一个异常被引发,并由此产生错误信息时,<br>
你可能仍然看不出哪里出错了。比如这例子,直接通过最后的异常看不到问题具体出在哪,这个时候就要考虑继续往上看了。</p>
<h3 id="python--中有哪些常见的异常类型">Python中有哪些常见的异常类型</h3>
<p>在编程时,知道如何在程序引发异常时读取 Python异常信息非常有用,如果再了解一些常见的异常类型那就更好了。<br>
有时候在面试的时候也会遇到提问 Python中常见的异常类型,以及其含义,所以这里也建议大家都了解以下。<br>
下面就列举一些出现频次高而且非常重要的异常类型,希望大家能够有一定的印象。</p>
<h4 id="attributeerror">AttributeError</h4>
<p>当你访问一个对象的属性,但是这个属性并没有在这个对象定义的时候,就会引发 AttributeError。<br>
下面是一个引发 AttributeError异常的示例:</p>
<pre><code>a= 1
a.b
</code></pre>
<p>运行之后引发异常</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line2, in<module>
a.b
AttributeError: 'int' objecthasnoattribute'b'
</code></pre>
<p>AttributeError的错误消息行告诉我们特定对象类型(在本例中为 int)没有访问的属性,<br>
在这个例子中属性为 b。点击文件链接可以快速定位到具体的错误代码的位置。</p>
<p>大多数情况下,引发这个异常表明你正在处理的对象可能不是你期望的类型。</p>
<pre><code>a_list= (1, 2)
a_list.append (3)
</code></pre>
<p>运行之后抛出异常信息</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line2, in<module>
a_list.append (3)
AttributeError: 'tuple' objecthasnoattribute'append'
</code></pre>
<p>这里尝试给 a_list对象进行 append操作但是引发了异常,<br>
这里的错误信息说,tuple对象没有 append属性。<br>
原因就是以为 a_list是列表但是实际上它是元组,<br>
元组是不可变类型不支持添加元素操作所以出错了。这里也告诉大家,以后定义变量名的时候也要主要规范问题,否则就容易出现这种,期望类型错误的情况。<br>
还有一种情况就是当对 None进行属性操作的时候,很容易引发上面的异常</p>
<pre><code>a_list= None
a_list.append (3)
</code></pre>
<p>运行抛出异常</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line2, in<module>
a_list.append (3)
AttributeError: 'NoneType' objecthasnoattribute'append'
</code></pre>
<p>是不是很眼熟啊,遇到这种情况不要慌,分析看看你的哪个对象是 None就好了。</p>
<h4 id="importerror">ImportError</h4>
<p>在使用 import导入模块时,如果要导入的模块找不到,或者从模块中导入模块中不存在的内容。这时就会触发 ImportError类型的错误或者它的子类 ModuleNotFoundError。</p>
<pre><code>importaaa
</code></pre>
<p>运行后输出</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line1, in<module>
importaaa
ModuleNotFoundError: Nomodulenamed'aaa'
</code></pre>
<p>在这个例子中可以看到,当我们使用 import导入一个不存在的模块时,就会出现 ModuleNotFoundError的错误,Traceback最下面一句信息给出了原因,<br>
没有名为 aaa的模块.<br>
然后我们再运行一个例子</p>
<pre><code>fromcollectionsimportasdf
</code></pre>
<p>运行之后的内容</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line1, in<module>
fromcollectionsimportasdf
ImportError: cannotimportname'asdf' from'collections'
</code></pre>
<p>根据前面的经验我们可以得知原因,不能从 collections模块中导入名为 asdf的模块。<br>
有时候为了程序能兼容在各个系统的时候,如果一个包找不到,找另一个的时候,比如在 windows中不能使用 ujson这个包,但是在 unix系统上是可以运行的,这个时候我们就可以使用下面的方法。</p>
<pre><code>try:
importujsonasjson
exceptImportErrorase:
importjson
</code></pre>
<p>首先导入 ujson然后使用 as给他重命名为 json,如果出现错误就会进入 except模块<br>
然后导入标准库的 json包,因为这边的库名已经叫 json了所以不用再重命名了。记住这个技巧非常的有用哦。</p>
<h4 id="indexerror">IndexError</h4>
<p>当你尝试从序列(如列表或元组)中检索索引,但是序列中找不到该索引。此时就会引发 IndexError。<br>
例如</p>
<pre><code>a_list= ['a', 'b']
a_list
</code></pre>
<p>运行之后的结果</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line2, in<module>
a_list
IndexError: listindexoutofrange
</code></pre>
<p>通过 IndexError的错误消息的最后一不能得到一个准确的信息,只知道一个超出范围的序列引用以及序列的类型,在本例中是一个列表。我们需要往上阅读错误信息,才能确定错误的具体位置。这里我们得知错误代码是 a_list,原因是索引3 超出了列表的范围,因为最大就是1(索引下标从0 开始的)。</p>
<h4 id="keyerror">KeyError</h4>
<p>与 IndexError类似,当你访问映射(通常是 dict )中不包含的键时,就会引发 KeyError。</p>
<pre><code>a_dict={}
a_dict['b']
</code></pre>
<p>运行之后</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line2, in<module>
a_dict['b']
KeyError: 'b'
</code></pre>
<p>KeyError的错误消息行给出找不到关键字 b。并没有太多的内容,但是,结合上面的错误信息,就可以解决这个问题。</p>
<h4 id="nameerror">NameError</h4>
<p>当你引用了变量、模块、类、函数或代码中没有定义的其他名称时,将引发 NameError。</p>
<pre><code>defgreet (person ):
print (f'Hello, {persn}')
greet ('World')
</code></pre>
<p>运行之后</p>
<pre><code>Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line3, in<module>
greet ('World')
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line2, ingreet
print (f'Hello, {persn}')
NameError: name'persn' isnotdefined
</code></pre>
<p>NameErrortraceback的错误消息行给出了缺失的名称 persn。<br>
这个例子中,在 print使用了没有定义过的变量 persn所以出现了错误。<br>
一般在拼写变量名出现问题时会引发这种错误。</p>
<h4 id="syntaxerror">SyntaxError</h4>
<p>当代码中有不正确的 Python语法时,就会引发 SyntaxError。<br>
下面的问题是函数定义行末尾缺少一个冒号。</p>
<pre><code>defgreet (person )
</code></pre>
<p>运行之后</p>
<pre><code>File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line1
defgreet (person )
^
SyntaxError: invalidsyntax
</code></pre>
<p>SyntaxError的错误消息行只告诉你代码的语法有问题。查看上面的行才能得到问题所在的行,通常会用一个^(插入符号)指向问题点。<br>
此外,细心的朋友会注意到,在 SyntaxError异常内容的第一行没有了之前的(mostrecentcalllast )。<br>
这是因为 SyntaxError是在 Python尝试解析代码时引发的,实际上代码并没有执行。</p>
<h4 id="typeerror">TypeError</h4>
<p>当你的代码试图对一个无法执行此操作的对象执行某些操作时,例如将字符串添加到整数中,以及一开始的例子使用 append方法给元组添加元素,这些都会引发 TypeError。<br>
以下是引发 TypeError的几个示例:</p>
<pre><code>>>> 1 + '1'
Traceback(mostrecentcalllast ):
File"<stdin>", line1, in<module>
TypeError: unsupportedoperandtype (s ) for+: 'int' and'str'
>>> '1' + 1
Traceback(mostrecentcalllast ):
File"<stdin>", line1, in<module>
TypeError: mustbestr, notint
>>> len (1)
Traceback(mostrecentcalllast ):
File"<stdin>", line1, in<module>
TypeError: objectoftype'int' hasnolen ()
</code></pre>
<p>以上所有引发类型错误的示例都会产生包含不同消息的错误消息行。它们每一个都能很好地告诉你哪里出了问题。<br>
前两个示例尝试将字符串和整数相加。然而,它们有细微的不同</p>
<ul>
<li>第一个是尝试在 int中拼接一个 str。</li>
<li>第二个是尝试在 str中拼接一个 int。</li>
</ul>
<p>错误消息行反映了这些差异。<br>
最后一个示例尝试在 int上调用 len ()。<br>
错误消息行告诉我们不能使用 int执行此操作。</p>
<h4 id="valueerror">ValueError</h4>
<p>当对象的值不正确时就会引发 ValueError。这个和我们前面说的因为索引的值不在序列的范围内,而导致 IndexError异常类似。<br>
下面看两个例子</p>
<pre><code>>>> a, b, c=
Traceback(mostrecentcalllast ):
File"<stdin>", line1, in<module>
ValueError: notenoughvaluestounpack(expected3, got2)
>>> a, b=
Traceback(mostrecentcalllast ):
File"<stdin>", line1, in<module>
ValueError: toomanyvaluestounpack(expected2)
</code></pre>
<p>这些示例中的 ValueError错误消息行可以准确地告诉我们值的一些问题:<br>
在第一个示例中,错误信息行是没有足够多的值去 unpack (解包)。 括号理面详细的写了你希望解包3 个值但实际上只给了2 个。<br>
第二个示例中,错误信息行是解包太多的值。先解包3 个值但是只给了2 个变量,所以括号里提示 expected2 就是说期望的实际是解包2 个值。<br>
上面这些错误类型,基本上都是基础遇到的,希望大家能熟悉记忆。</p>
<h3 id="如何记录这些错误信息呢">如何记录这些错误信息呢?</h3>
<p>前面我们说了很多异常的相关知识,但是我们应该如何利用好呢,这里我们就重点说一下,如何通过记录异常信息,方便后期程序的调试。<br>
下面让我们看一个关于使用 request模块的例子。<br>
首先需要导入 requests包,使用 pip即可。</p>
<pre><code>importrequests
url= "http://wwww.baidu.com"
response= requests.get (url )
print (response.status_code, response.text )
</code></pre>
<p>这是一个访问百度的例子,运行之后,我们成功获取了他的状态码和网页源码。<br>
接下来我们对 url进行修改然后再运行。</p>
<pre><code>importrequests
url= "http://urlis 233.com"
response= requests.get (url )
print (response.status_code, response.text )
</code></pre>
<p>运行之后我们发现程序出现了错误,下面分析下这些错误信息</p>
<pre><code>省略前面部分
Duringhandlingoftheaboveexception, anotherexceptionoccurred:
Traceback(mostrecentcalllast ):
File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line3, in<module>
response= requests.get (url )
File"/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/api.py", line75, inget
returnrequest ('get', url, params=params, **kwargs )
File"/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/api.py", line60, inrequest
returnsession.request (method=method, url=url, **kwargs )
File"/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/sessions.py", line533, inrequest
resp= self.send (prep, **send_kwargs )
File"/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/sessions.py", line646, insend
r= adapter.send (request, **kwargs )
File"/Users/chenxiangan/pythonproject/demo/venv/lib/python 3.7/site-packages/requests/adapters.py", line516, insend
raiseConnectionError (e, request=request )
requests.exceptions.ConnectionError: HTTPConnectionPool (host='urlis 233.com', port=80): Maxretriesexceededwithurl: / (CausedbyNewConnectionError ('<urllib 3.connection.HTTPConnectionobjectat0x 10faeba 90>: Failedtoestablishanewconnection: nodenamenorservnameprovided, ornotknown'))
</code></pre>
<p>这个错误信息很长,它引发了许多其他的异常,最终的异常类型是 requests.exceptions.ConnectionError。<br>
往前面的错误信息找可以发现问题代码,</p>
<pre><code>File"/Users/chenxiangan/pythonproject/demo/exmpale.py", line3, in<module>
response= requests.get (url )
</code></pre>
<p>进而定位到错误,这个错误原因主要是不存在地址"http://urlis 233.com",所以访问失败。</p>
<p>错误我们清楚了,但是一大堆的错误信息搭载控制台上,这样看很不美观,而且因为异常的原因我们的程序中断了。这个时候我们就可以使用 Python中的异常处理模块 try/except将代码改成下面这样</p>
<pre><code>importrequests
url= "http://urlis 233.com"
try:
response= requests.get (url )
exceptrequests.exceptions.ConnectionError:
print ("-1","链接有问题,访问失败")
else:
print (response.status_code, response.text )
</code></pre>
<p>再次运行可以得到下面的结果</p>
<pre><code>-1 链接有问题,访问失败
</code></pre>
<p>ok,我们的程序可以正常运行了,输出的信息也美观了。<br>
但是,在大多数实际系统中,我们不希望只是打印捕获的错误信息到控制台上,而是希望记录这些信息,方便后面的错误排查,所以最好的方案就是通过日志的方式记录这些程序中的异常。</p>
<p>你可以通过导入 logging模块,记录这些错误,最终代码如下</p>
<pre><code>importlogging
importrequests
logger= logging.getLogger (__name__)
url= "http://urlis 233.com"
try:
response= requests.get (url )
exceptrequests.exceptions.ConnectionErrorase:
logger.exception ()
print (-1, '链接有问题,访问失败')
else:
print (response.status_code, response.content )
</code></pre>
<p>现在,当你再运行有问题的 URL的脚本时,不仅会打印错误,同时还会在日志文件中记录这些错误信息。过于日志的其他信息可以看我之前的文章。</p>
<h3 id="总结">总结</h3>
<p>Python的 Traceback包含很多的信息,它可以帮助你找到 Python代码中出现的问题。 这些错误信息可能看起来有点令人生畏,但是一旦你分解它然后去理解,你就会发现它们非常的有用。<br>
希望大家能够多多自己去尝试理解错误信息,以及如何处理这种错误。</p>
<p>添加微信:回复:进群 即可加入Python学习群,更多技术文章,关注公众号:python学习开发。</p><br><br>
来源:https://www.cnblogs.com/c-x-a/p/11338945.html
頁:
[1]