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 | 打印当前函数的参数列表 |
下面结合具体实例说明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的调试工具。当然也可以使用日志功能达到调试的目的。
相关文章推荐
- Python进阶(三)Python语法
- 十分钟学习Python的进阶语法
- python进阶之内置函数和语法糖触发魔法方法
- Python进阶(三)——Python语法
- Python3入门与进阶笔记(七):高级语法
- Python 进阶语法八 —— global 和 return
- 十分钟学习Python的进阶语法
- Python进阶(三)——Python语法
- Python进阶语法
- Python 进阶语法
- Python进阶语法 五 —— set(集合)
- Python进阶(三)——Python语法
- Python进阶语法
- Python 进阶语法三——生成器
- Python 进阶语法七 —— 装饰器
- Python 进阶语法六 —— 三元运算符
- 十分钟学习Python的进阶语法
- Python进阶(三)——Python语法
- Python进阶With语法