软件测试 | 程序报错不要慌

122 阅读4分钟

我们找了一本Python教程,按照书上的例子一行一行敲下来,结果一运行却出错了。报错对于初学编程的人来说是恐惧的。那么如何应对常见的几类错误。

缩进错误

在Python程序中,不需要用“{}”来表示一个语句体,也不需要用“;”来表示一个语句的结束。Python对程序的缩进有着严格的要求,但你有时候却并不容易发现缩进问题。

如图1.16所示,程序看上去没有任何问题,但运行的时候却出现了“IndentationError:unindent does not match any outer indentation level”的错误。其实错误信息描述,已经很清楚了,但新手往往很难发现错误的原因。

图1.16 Python程序报错

如果将程序全选(快捷键Ctrl+a),就会发现错误,如图1.17所示。在add()函数的语句体中,“c=a+b”前面是一个Tab的间距,而“return c”前面是四个空格的间距。所以,虽然看上去缩进是对齐的,但它们却使用了不同的缩进方法,因而会导致Python执行错误。

图1.17 Sublime Test中的全选

引包错误

引包错误也是新手经常遇到的一类问题,但这其中有一个大坑。

import unittest

class test (unittest.TestCase):
  pass

运行程序。

Traceback (most recent call last):
 File "D:\pydemo\unittest.py",line 1,in <module>
   import unittest
 File "C:\pydemo\unittest.py",line 3,in <module>
   class test(unittest.TestCase):
AttributeError:model 'unittest' has no attribute 'TestCase'

我们要引用的明明是Python自带的unittest模块,然而程序却提示“module ‘unittest’has no attribute TestCase”。这个错误与Python的引包机制有关,当在程序中“import”一个模块或库时,Python首先会查找当前目录下是否存在同目录的Python文件,如果存在则会优先引用当前目录下的同名文件。

显然,我把自己写的程序文件命名为了“unittest.py”,在程序中又引用“unittest”,那么这就相当于自引用了。而我的真实意图是引用Python的unittest模块。当然,有时也不一定是自引用,也可能是程序的所在目录下出现了重名文件或目录。所以,在给编写的程序文件命名时一定要注意。

编码错误

在开发Python程序的过程中,会涉及三个方面的编码,具体如下。

(1)Python程序文件编码

编写的程序本身也存在编码,一般可以在程序的开头加上“#coding=utf-8”或“#coding=gbk”,使程序统一为UTF-8或GBK编码。

(2)Python程序运行环境(IDE)编码

不管是Python自带的IDLE或是PyCharm,使用的IDE本身也会有编码。所以要清楚地知道自己的IDE所使用的编码。

(3)Python程序读取外部文件、网页的编码。

最容易出现编码问题的情况应该是Python读取外部文件、网页的时候。首先要确定读取的文件、网页的编码格式,然后通过decode()和encode()方法来进行编码转换。

decode的作用是将其他编码的字符串转换成Unicode编码。

encode的作用是将Unicode编码转换成其他编码的字符串。

当我们再遇到Python的编码问题时,从以上三个方面分析就可以很容易地解决了。

学会分析错误

新手往往在看到程序抛出的一大堆报错时会变得手足无措,比起一大堆的报错,最难解决的问题是没有任何报错信息,而程序却无法正确地执行。如果能正确认真阅读报错信息,一般会很容易找到出现错误的原因。

......
Traceback (most recent call last)
  File "C:\Python35\lib\site-packages\diango\core\handlers\exception.py",
line 39, in inner
    response = get_response(request)
  File "C:\Python35\lib\site-packages\django\core\handlers\base.py",line 249,
in _legacy_get_responce
   responce = self._get_response(request)
  File "C:\Python35\lib\site-packages\django\core\handlers\base.py",line 187,
in _get_responce
    responce = self.process_exception_by_middeware(e,request)
  File "C:\Python35\lib\site-packages\django\core\handlers\base.py",line 185,
in _get_responce
  responce = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Python35\lib\site-packages\django\contrib\auth\decorators.py",
line 23, in _wrapped_view
  return view_func(request, *args, **kwargs)
  File "D:\git\guest\sign\views.py",line 85,in search_phone
    print(phone)
NameError:name 'phone' is not defind

上面是Django的一段报错信息。在分析报错信息时,一般要遵循以下两点。

(1)找到自己写的程序。所以前面的一大段信息就没必要看了;根据倒数第三行的提示:“File ”D:\git\guest\sign\views.py", line 85,in search_phone"(views.py 文件的倒数第85行,在search_phone函数中,找到自己写的代码“print(phone)”)。

(2)看最终的错误提示。最终的提示为“NameError: name 'phone' is not defind”。"NameError"为错误类型,根据错误类型可以锁定错误范围。“name ‘phone’is not defined”为错误提示(名字‘phone’没有定义)。结合第一点找到自己写的程序,显然,print()打印的‘phone’变量没有定义。

搜索微信公众号:TestingStudio霍格沃兹的干货都很硬核