您的位置:首页 > 编程语言 > Python开发

Python进阶语法二

2016-11-27 12:34 429 查看

二、调试(Debugging)

利用好调试,能大大提高你捕捉代码Bug的。而大部分初学者则忽略了Python Debugger(pdb)的重要性。

2.1 在命令行中运行

你可以在命令行中使用Python Debugger运行一个脚本,例如:
$ python -m pdb my_script.py

这会触发debugger在脚本第一行指令处停止执行。这在脚本很短时会很有帮助。你可以通过Pdb模式接着查看变量信息,并且逐行调试。

2.2 在脚本内部运行

你可以在脚本内部设置断点,这样就可以在某些特定点查看变量信息和各种执行时的信息了。这里将使用pdb.set_trace()方法来实现。举个例子:

import pdb
​
def demo(a,b):
c = a + b
pdb.set_trace()
return "c = ",c
print demo(2, 3)

> <ipython-input-1-3bbd09d9b118>(6)demo()
-> return "c = ",c
(Pdb) a
a = 2
b = 3
(Pdb) w
/Users/dfsj/anaconda2/anaconda/lib/python2.7/runpy.py(174)_run_module_as_main()
-> "__main__", fname, loader, pkg_name)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/runpy.py(72)_run_code()
-> exec code in run_globals
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/__main__.py(3)<module>()
-> app.launch_new_instance()
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/traitlets/config/application.py(653)launch_instance()
-> app.start()
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/kernelapp.py(474)start()
-> ioloop.IOLoop.instance().start()
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/zmq/eventloop/ioloop.py(162)start()
-> super(ZMQIOLoop, self).start()
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/tornado/ioloop.py(887)start()
-> handler_func(fd_obj, events)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/tornado/stack_context.py(275)null_wrapper()
-> return fn(*args, **kwargs)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/zmq/eventloop/zmqstream.py(440)_handle_events()
-> self._handle_recv()
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/zmq/eventloop/zmqstream.py(472)_handle_recv()
-> self._run_callback(callback, msg)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/zmq/eventloop/zmqstream.py(414)_run_callback()
-> callback(*args, **kwargs)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/tornado/stack_context.py(275)null_wrapper()
-> return fn(*args, **kwargs)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/kernelbase.py(276)dispatcher()
-> return self.dispatch_shell(stream, msg)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/kernelbase.py(228)dispatch_shell()
-> handler(stream, idents, msg)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/kernelbase.py(390)execute_request()
-> user_expressions, allow_stdin)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/ipkernel.py(196)do_execute()
-> res = shell.run_cell(code, store_history=store_history, silent=silent)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/zmqshell.py(501)run_cell()
-> return super(ZMQInteractiveShell, self).run_cell(*args, **kwargs)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/IPython/core/interactiveshell.py(2717)run_cell()
-> interactivity=interactivity, compiler=compiler, result=result)
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/IPython/core/interactiveshell.py(2821)run_ast_nodes()
-> if self.run_code(code, result):
/Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/IPython/core/interactiveshell.py(2881)run_code()
-> exec(code_obj, self.user_global_ns, self.user_ns)
<ipython-input-1-3bbd09d9b118>(7)<module>()
-> print demo(2, 3)
> <ipython-input-1-3bbd09d9b118>(6)demo()
-> return "c = ",c
(Pdb) s
--Return--
> <ipython-input-1-3bbd09d9b118>(6)demo()->('c = ', 5)
-> return "c = ",c
(Pdb) n
--Call--
> /Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/iostream.py(309)write()
-> def write(self, string):
(Pdb) n
> /Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/iostream.py(310)write()
-> if self.pub_thread is None:
(Pdb) n
> /Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/iostream.py(314)write()
-> if not isinstance(string, unicode_type):
(Pdb) n
> /Users/dfsj/anaconda2/anaconda/lib/python2.7/site-packages/ipykernel/iostream.py(315)write()
-> string = string.decode(self.encoding, 'replace')
(Pdb) c
('c = ', 5)

运行上面的脚本后,就会在运行时马上进入debugger模式。debugger模式下的一些命令如下:
命令列表:

命令解释
break 或 b设置断点
continue 或 c继续执行程序
list 或 l查看当前行的代码段
step 或 s执行当前代码行,并停在第一个能停的地方(相当于单步进入)
return 或 r执行代码直到从当前函数返回
exit 或 q中止并退出
next 或 n继续执行到当前函数的下一行,或者当前行直接返回(单步跳过)
pp打印变量的值
help帮助
w显示当前正在执行的代码行的上下文信息
a打印当前函数的参数列表
单步跳过(next)和单步进入(step)的区别在于,单步进入会进入当前行调用的函数内部并停在里面,而单步跳过(几乎)全速执行完当前行调用的函数,并停在当前函数的下一行。

下面结合具体实例说明pdb使用方法,实例参考Python代码调试技巧

1、测试代码示例

import pdb
a = "aaa"
pdb.set_trace()
b = "bbb"
c = "ccc"
final = a + b + c
print final

开始调试: 将上面代码保存为"epdb1.py",直接运行脚本,会停留在pdb_trace()处,选择“n”,并回车就可以执行当前的statement。在第一次按下了“n+Enter”后,下一次可以直接“Enter”表示重复执行上一条debug命令,也可以通过上下方向键选择之前已经输入过的命令。

利用pdb调试:

dfsjdeMacBook-Pro:~ dfsj$ python epdb1.py
> /Users/dfsj/epdb1.py(4)<module>()
-> b = "bbb"
(Pdb) n
> /Users/dfsj/epdb1.py(5)<module>()
-> c = "ccc"
(Pdb)
> /Users/dfsj/epdb1.py(6)<module>()
-> final = a + b + c
(Pdb) list
1  	import pdb
2  	a = "aaa"
3  	pdb.set_trace()
4  	b = "bbb"
5  	c = "ccc"
6  ->	final = a + b + c
7  	print final
8
[EOF]
(Pdb) n
> /Users/dfsj/epdb1.py(7)<module>()
-> print final
(Pdb) c
aaabbbccc

退出debug: 使用quit或者q可以退出当前的debug,但是quit会以一种非常粗鲁的方式退出程序,其结果是直接crash。

dfsjdeMacBook-Pro:~ dfsj$ python epdb1.py
> /Users/dfsj/epdb1.py(4)<module>()
-> b = "bbb"
(Pdb) n
> /Users/dfsj/epdb1.py(5)<module>()
-> c = "ccc"
(Pdb) q
Traceback (most recent call last):
File "epdb1.py", line 5, in <module>
c = "ccc"
File "epdb1.py", line 5, in <module>
c = "ccc"
File "/Users/dfsj/anaconda2/anaconda/lib/python2.7/bdb.py", line 49, in trace_dispatch
return self.dispatch_line(frame)
File "/Users/dfsj/anaconda2/anaconda/lib/python2.7/bdb.py", line 68, in dispatch_line
if self.quitting: raise BdbQuit
bdb.BdbQuit

**打印变量的值:**如果需要在调试过程中打印变量的值,可以直接使用p加上变量名,但是需要注意的是打印仅仅在当前的statement已经被执行了之后才能看到具体的值,否则会报 NameError: <exceptions.NameError ... ...> 错误。

dfsjdeMacBook-Pro:~ dfsj$ python epdb1.py
> /Users/dfsj/epdb1.py(4)<module>()
-> b = "bbb"
(Pdb) n
> /Users/dfsj/epdb1.py(5)<module>()
-> c = "ccc"
(Pdb) p b
'bbb'
(Pdb)
'bbb'
(Pdb) n
> /Users/dfsj/epdb1.py(6)<module>()
-> final = a + b + c
(Pdb) p c
'ccc'
(Pdb) p final
*** NameError: NameError("name 'final' is not defined",)
(Pdb) n
> /Users/dfsj/epdb1.py(7)<module>()
-> print final
(Pdb) p final
'aaabbbccc'
(Pdb)

**继续执行:**使用c可以停止当前的debug使程序继续执行。如果在下面的程序中继续有set_statement()的声明,则又会重新进入到debug的状态,读者可以在代码 print final 之前再加上 set_trace()验证。

dfsjdeMacBook-Pro:~ dfsj$ python epdb1.py
> /Users/dfsj/epdb1.py(4)<module>()
-> b = "bbb"
(Pdb) n
> /Users/dfsj/epdb1.py(5)<module>()
-> c = "ccc"
(Pdb) c
> /Users/dfsj/epdb1.py(8)<module>()
-> print final
(Pdb) c
aaabbbccc

显示代码: 在debug的时候不一定能记住当前的代码块,如果需要要查看具体的代码块,则可以通过使用list或者l命令显示。list会用箭头->指向当前debug的语句。

dfsjdeMacBook-Pro:~ dfsj$ python epdb1.py
> /Users/dfsj/epdb1.py(4)<module>()
-> b = "bbb"
(Pdb) list
1  	import pdb
2  	a = "aaa"
3  	pdb.set_trace()
4  ->	b = "bbb"
5  	c = "ccc"
6  	final = a + b + c
7  	pdb.set_trace()
8  	print final
9
[EOF]
(Pdb) c
> /Users/dfsj/epdb1.py(8)<module>()
-> print final
(Pdb) list
3  	pdb.set_trace()
4  	b = "bbb"
5  	c = "ccc"
6  	final = a + b + c
7  	pdb.set_trace()
8  ->	print final
9
[EOF]
(Pdb)

在使用函数的情况下进行debug代码清单如下:

import pdb
def combine(s1,s2):    # define subroutine combine, which...
s3 = s1 + s2 + s1    # sandwiches s2 between copies of s1, ...
s3 = '"' + s3 +'"'   # encloses it in double quotes,...
return s3            # and returns it.
a = "aaa"
pdb.set_trace()
b = "bbb"
c = "ccc"
final = combine(a,b)
print final

对函数进行debug:如果直接使用n进行debug则到 final=combine(a,b)这句的时候会将其当做普通的赋值语句,进入到print final。如果想要对函数进行debug如何处理呢?可以直接使用s进入函数块。函数里面的单步调试与上面的介绍类似。如果不想在函数里单步调试可以在断点处直接按“r”退出到调用的地方。

dfsjdeMacBook-Pro:~ dfsj$ python epdb2.py
> /Users/dfsj/epdb2.py(8)<module>()
-> b = "bbb"
(Pdb) n
> /Users/dfsj/epdb2.py(9)<module>()
-> c = "ccc"
(Pdb) n
> /Users/dfsj/epdb2.py(10)<module>()
-> final = combine(a, b)
(Pdb) s
--Call--
> /Users/dfsj/epdb2.py(2)combine()
-> def combine(s1, s2):        # define subroutine combine, which...
(Pdb) n
> /Users/dfsj/epdb2.py(3)combine()
-> s3 = s1 + s2 + s1      # sandwiches s2 between copies of s1, ...
(Pdb) list
1  	import pdb
2  	def combine(s1, s2):        # define subroutine combine, which...
3  ->	    s3 = s1 + s2 + s1      # sandwiches s2 between copies of s1, ...
4  	    s3 = '"' + s3 + '"'    # enclose it in double quotes, ...
5  	    return s3
6  	a = "aaa"
7  	pdb.set_trace()
8  	b = "bbb"
9  	c = "ccc"
10  	final = combine(a, b)
11  	print final
(Pdb) n
> /Users/dfsj/epdb2.py(4)combine()
-> s3 = '"' + s3 + '"'    # enclose it in double quotes, ...
(Pdb) n
> /Users/dfsj/epdb2.py(5)combine()
-> return s3
(Pdb) n
--Return--
> /Users/dfsj/epdb2.py(5)combine()->'"aaabbbaaa"'
-> return s3
(Pdb) n
> /Users/dfsj/epdb2.py(11)<module>()
-> print final
(Pdb)

pdb调试有个明显的缺陷就是对于多线程,远程调试等支持的不够好,同时没有较为直观的界面显示,不太适合大型的python项目。贰在较大的python项目中,这些调试需求比较常见,因此需要使用高级的调试工具,比如可以使用PyCharm IDE的调试工具。当然也可以使用日志功能达到调试的目的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: