调试和分析Python脚本

20次阅读

共计 6739 个字符,预计需要花费 17 分钟才能阅读完成。

来源 | 愿码 (ChainDesk.CN) 内容编辑
愿码 Slogan | 连接每个程序员的故事
网站 | http://chaindesk.cn

愿码愿景 | 打造全学科 IT 系统免费课程,助力小白用户、初级工程师 0 成本免费系统学习、低成本进阶,帮助 BAT 一线资深工程师成长并利用自身优势创造睡后收入。
官方公众号 | 愿码 | 愿码服务号 | 区块链部落
免费加入愿码全思维工程师社群 | 任一公众号回复“愿码”两个字获取入群二维码

本文阅读时长:11min
调试和分析在 Python 开发中发挥重要作用。调试器可帮助程序员分析完整的代码。调试器设置断点,而分析器运行我们的代码并向我们提供执行时间的详细信息,分析器将识别程序中的瓶颈。
Python 调试技术

调试是一个解决代码中出现的问题并阻止软件正常运行的过程。在 Python 中,调试非常简单。Python 调试器设置条件断点并一次调试一行源代码。我们将使用 pdb Python 标准库中的模块调试我们的 Python 脚本。
为了更好地调试 Python 程序,可以使用各种技术。我们将讨论 Python 调试的四种技术:

print() 声明:这是了解发生了什么的最简单方法,因此您可以检查已执行的内容。

logging:这就像一个 print 声明,但有更多的上下文信息,所以你可以完全理解它。

pdb debugger:这是一种常用的调试技术。使用的优点 pdb 是您可以 pdb 从命令行,解释器和程序中使用。
IDE 调试器:IDE 具有集成调试器。它允许开发人员执行他们的代码,然后开发人员可以在程序执行时进行检查。

错误处理(异常处理)

在本节中,我们将学习 Python 如何处理异常。例外是程序执行期间发生的错误。每当发生任何错误时,Python 都会生成一个异常,该异常将使用 try … except 块进行处理。程序无法处理某些异常,因此会导致错误消息。现在,我们将看到一些异常示例。
在终端中,启动 python3 交互式控制台,我们将看到一些异常示例:
student@ubuntu:~$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>>
>>> 50 / 0

Traceback (most recent call last):
File “”, line 1, in ZeroDivisionError: division by zero
>>>
>>> 6 + abc*5
Traceback (most recent call last):
File “”, line 1, in NameError: name ‘abc’ is not defined
>>>
>>> ‘abc’ + 2
Traceback (most recent call last):
File “”, line 1, in TypeError: Can’t convert ‘int’ object to str implicitly
>>>
>>> import abcd
Traceback (most recent call last):
File “”, line 1, in ImportError: No module named ‘abcd’
>>>
这些是例外的一些例子。现在,我们将看到我们如何处理异常。
每当 Python 程序中发生错误时,都会引发异常。我们还可以使用 raise 关键字强制引发异常。
现在我们将看到一个 try…except 处理异常的块。在 try 块中,我们将编写可能生成异常的代码。在 except 块中,我们将为该异常编写解决方案。
语法 try…except 如下:
try:
statement(s)
except:
statement(s)
一个 try 块可以有多个 except 语句。我们也可以通过在 except 关键字后面输入例外名称来处理特定的例外。处理特定异常的语法如下:
try:
statement(s)
except exception_name:
statement(s)
我们将创建一个 exception_example.py 要捕获的脚本 ZeroDivisionError。在脚本中编写以下代码:
a = 35
b = 57
try:
c = a + b
print(“The value of c is: “, c)
d = b / 0
print(“The value of d is: “, d)

except:
print(“Division by zero is not possible”)

print(“Out of try…except block”)
按如下所示运行脚本,您将获得以下输出:
student@ubuntu:~$ python3 exception_example.py
The value of c is: 92
Division by zero is not possible
Out of try…except block
调试器工具

Python 支持许多调试工具:

winpdb
pydev
pydb
pdb
gdb
pyDebug

在本节中,我们将学习 pdb Python 调试器。pdbmodule 是 Python 标准库的一部分,始终可供使用。
pdb 调试器
该 pdb 模块用于调试 Python 程序。Python 程序使用 pdb 交互式源代码调试器来调试程序。pdb 设置断点并检查堆栈帧,并列出源代码。
现在我们将了解如何使用 pdb 调试器。有三种方法可以使用此调试器:
· 在解释器中
· 从命令行
· 在 Python 脚本中
我们将创建一个 pdb_example.py 脚本并在该脚本中添加以下内容:
class Student:
def __init__(self, std):
self.count = std

def print_std(self):
for i in range(self.count):
print(i)
return
if __name__ == ‘__main__’:
Student(5).print_std()
以此脚本为例学习 Python 调试,我们将看到如何详细启动调试器。
在解释器中

要从 Python 交互式控制台启动调试器,我们使用 run()或 runeval()。
启动 python3 交互式控制台。运行以下命令以启动控制台:
$ python3
导入我们的 pdb_example 脚本名称和 pdb 模块。现在,我们将使用 run()并且我们将字符串表达式作为参数传递给 run()Python 解释器本身:
student@ubuntu:~$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>>
>>> import pdb_example
>>> import pdb
>>> pdb.run(‘pdb_example.Student(5).print_std()’)
> (1)()
(Pdb)
要继续调试,请 continue 在(Pdb)提示符后输入并按 Enter 键。如果你想知道我们可以在这里使用的选项,那么在(Pdb)提示后按两次 Tab 键。
现在,输入后 continue,我们将获得如下输出:
student@ubuntu:~$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>>
>>> import pdb_example
>>> import pdb
>>> pdb.run(‘pdb_example.Student(5).print_std()’)
> (1)()
(Pdb) continue
0
1
2
3
4
>>>
从命令行

运行调试器的最简单,最直接的方法是从命令行。我们的程序将作为调试器的输入。您可以从命令行使用调试器,如下所示:
$ python3 -m pdb pdb_example.py
从命令行运行调试器时,将加载源代码,它将停止在找到的第一行执行。输入 continue 以继续调试。这是输出:
student@ubuntu:~$ python3 -m pdb pdb_example.py
> /home/student/pdb_example.py(1)()
-> class Student:
(Pdb) continue
0
1
2
3
4
The program finished and will be restarted
> /home/student/pdb_example.py(1)()
-> class Student:
(Pdb)
在 Python 脚本中

前两种技术将在 Python 程序开始时启动调试器。但这第三种技术最适合长期运行的流程。要在脚本中启动调试器,请使用 set_trace()。
现在,修改您的 pdb_example.py 文件,如下所示:
import pdb
class Student:
def __init__(self, std):
self.count = std

def print_std(self):
for i in range(self.count):
pdb.set_trace()
print(i)
return

if __name__ == ‘__main__’:
Student(5).print_std()
现在,按如下方式运行程序:
student@ubuntu:~$ python3 pdb_example.py
> /home/student/pdb_example.py(10)print_std()
-> print(i)
(Pdb) continue
0
> /home/student/pdb_example.py(9)print_std()
-> pdb.set_trace()
(Pdb)
set_trace() 是一个 Python 函数,因此您可以在程序中的任何位置调用它。
因此,这些是启动调试器的三种方式。
调试基本程序崩溃

在本节中,我们将看到跟踪模块。跟踪模块有助于跟踪程序执行。因此,每当您的 Python 程序崩溃时,我们都可以理解崩溃的位置。我们可以通过将跟踪模块导入您的脚本以及命令行来使用它。
现在,我们将创建一个名为脚本 trace_example.py 并在脚本中编写以下内容:
class Student:
def __init__(self, std):
self.count = std

def go(self):
for i in range(self.count):
print(i)
return
if __name__ == ‘__main__’:
Student(5).go()
输出如下:
student@ubuntu:~$ python3 -m trace –trace trace_example.py
— modulename: trace_example, funcname: trace_example.py(1): class Student:
— modulename: trace_example, funcname: Student
trace_example.py(1): class Student:
trace_example.py(2): def __init__(self, std):
trace_example.py(5): def go(self):
trace_example.py(10): if __name__ == ‘__main__’:
trace_example.py(11): Student(5).go()
— modulename: trace_example, funcname: init
trace_example.py(3): self.count = std
— modulename: trace_example, funcname: go
trace_example.py(6): for i in range(self.count):
trace_example.py(7): print(i)
0
trace_example.py(6): for i in range(self.count):
trace_example.py(7): print(i)
1
trace_example.py(6): for i in range(self.count):
trace_example.py(7): print(i)
2
trace_example.py(6): for i in range(self.count):
trace_example.py(7): print(i)
3
trace_example.py(6): for i in range(self.count):
trace_example.py(7): print(i)
4
因此,通过 trace –trace 在命令行使用,开发人员可以逐行跟踪程序。因此,只要程序崩溃,开发人员就会知道崩溃的实例。
分析和计时程序

分析 Python 程序意味着测量程序的执行时间。它衡量每个功能所花费的时间。Python 的 cProfile 模块用于分析 Python 程序。
cProfile 模块
如前所述,分析意味着测量程序的执行时间。我们将使用 cProfile Python 模块来分析程序。
现在,我们将编写一个 cprof_example.py 脚本并在其中编写以下代码:
mul_value = 0
def mul_numbers(num1, num2):
mul_value = num1 * num2;
print (“Local Value: “, mul_value)
return mul_value
mul_numbers(58, 77)
print (“Global Value: “, mul_value)
运行程序,您将看到如下输出:
student@ubuntu:~$ python3 -m cProfile cprof_example.py
Local Value: 4466
Global Value: 0
6 function calls in 0.000 seconds
Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 cprof_example.py:1()
1 0.000 0.000 0.000 0.000 cprof_example.py:2(mul_numbers)
1 0.000 0.000 0.000 0.000 {built-in method builtins.exec}
2 0.000 0.000 0.000 0.000 {built-in method builtins.print}
1 0.000 0.000 0.000 0.000 {method ‘disable’ of ‘_lsprof.Profiler’ objects}
因此,使用时 cProfile,所有被调用的函数都将打印出每个函数所花费的时间。现在,我们将看到这些列标题的含义:
· ncalls:通话次数
· tottime:在给定函数中花费的总时间
· percall:商数 tottime 除以 ncalls
· cumtime:在此和所有方面花费的累计时间 subfunctions
· percall:cumtime 除以原始调用的商数
· filename:lineno(function):提供每个功能的相应数据
timeit
timeit 是一个 Python 模块,用于计算 Python 脚本的一小部分。您可以从命令行调用 timeit,也可以将 timeit 模块导入到脚本中。我们将编写一个脚本来计算一段代码。创建一个 timeit_example.py 脚本并将以下内容写入其中:
import timeit
prg_setup = “from math import sqrt”
prg_code = ”’
def timeit_example():
list1 = []
for x in range(50):
list1.append(sqrt(x))
”’
# timeit statement
print(timeit.timeit(setup = prg_setup, stmt = prg_code, number = 10000))
使用 timeit,我们可以决定我们要测量的代码片段。因此,我们可以轻松定义设置代码以及我们要单独执行测试的代码段。主代码运行 100 万次,这是默认时间,而设置代码只运行一次。
使程序运行得更快

有多种方法可以使 Python 程序运行得更快,例如:

描述您的代码,以便识别瓶颈
使用内置函数和库,因此解释器不需要执行循环
避免使用全局变量,因为 Python 在访问全局变量时非常慢
使用现有包

正文完
 0