下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922
论坛 >编程语言 >Python程序员必知必会的开发者工具 (1)

Python程序员必知必会的开发者工具 (1)

spring发布于 2018-01-16 09:35查看:1132回复:1

Python已经演化出了一个广泛的生态系统,该生态系统能够让Python程序员的生活变得更加简单,减少他们重复造轮的工作。同样的理念也适用于工具开发者的工作,即便他们开发出的工具并没有出现在最终的程序中。本文将介绍Python程序员必知必会的开发者工具。

对于开发者来说,最实用的帮助莫过于帮助他们编写代码文档了。模块可以根据源代码中的docstrings为任何可导入模块生成格式良好的文档。Python包含了两个测试框架来自动测试代码以及验证代码的正确性:1)模块,该模块可以从源代码或独立文件的例子中抽取出测试用例。2)模块,该模块是一个全功能的自动化测试框架,该框架提供了对测试准备(test fixtures), 预定义测试集(predefined test suite)以及测试发现(test discovery)的支持。

模块可以监控Python执行程序的方式,同时生成一个报表来显示程序的每一行执行的次数。这些信息可以用来发现未被自动化测试集所覆盖的程序执行路径,也可以用来研究程序调用图,进而发现模块之间的依赖关系。编写并执行测试可以发现绝大多数程序中的问题,Python使得debug工作变得更加简单,这是因为在大部分情况下,Python都能够将未被处理的错误打印到控制台中,我们称这些错误信息为traceback。如果程序不是在文本控制台中运行的,traceback也能够将错误信息输出到日志文件或是消息对话框中。当标准的traceback无法提供足够的信息时,可以使用cgitb 模块来查看各级栈和源代码上下文中的详细信息,比如局部变量。模块还能够将这些跟踪信息以HTML的形式输出,用来报告web应用中的错误。

一旦发现了问题出在哪里后,就需要使用到交互式调试器进入到代码中进行调试工作了,模块能够很好地胜任这项工作。该模块可以显示出程序在错误产生时的执行路径,同时可以动态地调整对象和代码进行调试。当程序通过测试并调试后,下一步就是要将注意力放到性能上了。开发者可以使用以及模块来测试程序的速度,找出程序中到底是哪里很慢,进而对这部分代码独立出来进行调优的工作。Python程序是通过解释器执行的,解释器的输入是原有程序的字节码编译版本。这个字节码编译版本可以在程序执行时动态地生成,也可以在程序打包的时候就生成。模块可以处理程序打包的事宜,它暴露出了打包相关的接口,该接口能够被安装程序和打包工具用来生成包含模块字节码的文件。同时,在开发环境中,compileall模块也可以用来验证源文件是否包含了语法错误。

在源代码级别,模块提供了一个类查看器,方便文本编辑器或是其他程序对Python程序中有意思的字符进行扫描,比如函数或者是类。在提供了类查看器以后,就无需引入代码,这样就避免了潜在的副作用影响。


文档字符串与doctest模块

如果函数,类或者是模块的第一行是一个字符串,那么这个字符串就是一个文档字符串。可以认为包含文档字符串是一个良好的编程习惯,这是因为这些字符串可以给Python程序开发工具提供一些信息。比如,help()命令能够检测文档字符串,Python相关的IDE也能够进行检测文档字符串的工作。由于程序员倾向于在交互式shell中查看文档字符串,所以最好将这些字符串写的简短一些。例如

image.png

在编写文档时,一个常见的问题就是如何保持文档和实际代码的同步。例如,程序员也许会修改函数的实现,但是却忘记了更新文档。针对这个问题,我们可以使用doctest模块。doctest模块收集文档字符串,并对它们进行扫描,然后将它们作为测试进行执行。为了使用doctest模块,我们通常会新建一个用于测试的独立的模块。例如,如果前面的例子Test class包含在文件mult.py中,那么,你应该新建一个testmult.py文件用来测试,如下所示:

image.png

在这段代码中,doctest.testmod(module)会执行特定模块的测试,并且返回测试失败的个数以及测试的总数目。如果所有的测试都通过了,那么不会产生任何输出。否则的话,你将会看到一个失败报告,用来显示期望值和实际值之间的差别。如果你想看到测试的详细输出,你可以使用testmod(module, verbose=True).

如果不想新建一个单独的测试文件的话,那么另一种选择就是在文件末尾包含相应的测试代码:

image.png

如果想执行这类测试的话,我们可以通过-m选项调用doctest模块。通常来讲,当执行测试的时候没有任何的输出。如果想查看详细信息的话,可以加上-v选项。

image.png

单元测试与unittest模块

如果想更加彻底地对程序进行测试,我们可以使用unittest模块。通过单元测试,开发者可以为构成程序的每一个元素(例如,独立的函数,方法,类以及模块)编写一系列独立的测试用例。当测试更大的程序时,这些测试就可以作为基石来验证程序的正确性。当我们的程序变得越来越大的时候,对不同构件的单元测试就可以组合起来成为更大的测试框架以及测试工具。这能够极大地简化软件测试的工作,为找到并解决软件问题提供了便利。

image.png

在使用单元测试时,我们需要定义一个继承自unittest.TestCase的类。在这个类里面,每一个测试都以方法的形式进行定义,并都以test打头进行命名——例如,’testsimplestring‘,’testtypeconvert‘以及类似的命名方式(有必要强调一下,只要方法名以test打头,那么无论怎么命名都是可以的)。在每个测试中,断言可以用来对不同的条件进行检查。

实际的例子:

假如你在程序里有一个方法,这个方法的输出指向标准输出(sys.stdout)。这通常意味着是往屏幕上输出文本信息。如果你想对你的代码进行测试来证明这一点,只要给出相应的输入,那么对应的输出就会被显示出来。

image.png

内置的print函数在默认情况下会往sys.stdout发送输出。为了测试输出已经实际到达,你可以使用一个替身对象对其进行模拟,并且对程序的期望值进行断言。unittest.mock模块中的patch()方法可以只在运行测试的上下文中才替换对象,在测试完成后就立刻返回对象原始的状态。下面是urlprint()方法的测试代码:

image.png

urlprint()函数有三个参数,测试代码首先给每个参数赋了一个假值。变量expected_url包含了期望的输出字符串。为了能够执行测试,我们使用了unittest.mock.patch()方法作为上下文管理器,把标准输出sys.stdout替换为了StringIO对象,这样发送的标准输出的内容就会被StringIO对象所接收。变量fake_out就是在这一过程中所创建出的模拟对象,该对象能够在with所处的代码块中所使用,来进行一系列的测试检查。当with语句完成时,patch方法能够将所有的东西都复原到测试执行之前的状态,就好像测试没有执行一样,而这无需任何额外的工作。但对于某些Python的C扩展来讲,这个例子却显得毫无意义,这是因为这些C扩展程序绕过了sys.stdout的设置,直接将输出发送到了标准输出上。这个例子仅适用于纯Python代码的程序(如果你想捕获到类似C扩展的输入输出,那么你可以通过打开一个临时文件然后将标准输出重定向到该文件的技巧来进行实现)。


Python调试器与pdb模块

Python在pdb模块中包含了一个简单的基于命令行的调试器。pdb模块支持事后调试(post-mortem debugging),栈帧探查(inspection of stack frames),断点(breakpoints),单步调试(single-stepping of source lines)以及代码审查(code evaluation)。

有都能够在程序中调用调试器,或是在交互式的Python终端中进行调试工作。

在所有启动调试器的函数中,函数set_trace()也许是最简易实用的了。如果在复杂程序中发现了问题,可以在代码中插入set_trace()函数,并运行程序。当执行到set_trace()函数时,这就会暂停程序的执行并直接跳转到调试器中,这时候你就可以大展手脚开始检查运行时环境了。当退出调试器时,调试器会自动恢复程序的执行。

假设你的程序有问题,你想找到一个简单的方法来对它进行调试。

如果你的程序崩溃时报了一个异常错误,那么你可以用python3 -i someprogram.py这个命令来运行你的程序,这能够很好地发现问题所在。-i选项表明只要程序终结就立即启动一个交互式shell。在这个交互式shell中,你就可以很好地探查到底发生了什么导致程序的错误。例如,如果你有以下代码:

image.png

如果使用python3 -i 命令运行程序就会产生如下输出:

image.png

如果你没有发现什么明显的错误,那么你可以进一步地启动Python调试器。例如:

image.png

如果你的代码身处的环境很难启动一个交互式shell的话(比如在服务器环境下),你可以增加错误处理的代码,并自己输出跟踪信息。例如:

image.png

如果你的程序并没有崩溃,而是说程序的行为与你的预期表现的不一致,那么你可以尝试在一些可能出错的地方加入print()函数。如果你打算采用这种方案的话,那么还有些相关的技巧值得探究。首先,函数traceback.print_stack()能够在被执行时立即打印出程序中栈的跟踪信息。例如:

image.png

另外,你可以在程序中任意一处使用pdb.set_trace()手动地启动调试器,就像这样:

image.png

在深入解析大型程序的时候,这是一个非常实用的技巧,这样操作能够清楚地了解程序的控制流或是函数的参数。比如,一旦调试器启动了之后,你就可以使用print或者w命令来查看变量,来了解栈的跟踪信息。

在进行软件调试时,千万不要让事情变得很复杂。有时候仅仅需要知道程序的跟踪信息就能够解决大部分的简单错误(比如,实际的错误总是显示在跟踪信息的最后一行)。在实际的开发过程中,将print()函数插入到代码中也能够很方便地显示调试信息(只需要记得在调试完以后将print语句删除掉就行了)。调试器的通用用法是在崩溃的函数中探查变量的值,知道如何在程序崩溃以后再进入到调试器中就显得非常实用。在程序的控制流不是那么清楚的情况下,你可以插入pdb.set_trace()语句来理清复杂程序的思路。本质上,程序会一直执行直到遇到set_trace()调用,之后程序就会立刻跳转进入到调试器中。在调试器里,你就可以进行更多的尝试。如果你正在使用Python的IDE,那么IDE通常会提供基于pdb的调试接口,你可以查阅IDE的相关文档来获取更多的信息。




收藏(0)0
查看评分情况

全部评分

此主贴暂时没有点赞评分

总计:0

回复分享

版主推荐

    共有1条评论

    • IT宅男
    • mr jack
    • Mr ken
    • Mright
    • cappuccino
    • YUI
    • 课课家运营团队
    • 课课家技术团队1
    • 酸酸~甜甜
    • 选择版块:

    • 标题:

    • 内容

    • 验证码:

    • 标题:

    • 内容

    • 选择版块:

    移动帖子x

    移动到: