前言
谈到奇技淫巧,我认为这款神器当之无愧。
在程序开发过程中,代码的运行往往会和我们预期的结果有所差别。于是,我们需要清楚代码运行过程中到底发生了什么?代码哪些模块运行了,哪些模块没有运行?输出的局部变量是什么样的?很多时候,我们会想到选择使用成熟、常用的IDE使用断点和watches调试程序,或者更为基础的print函数、log打印出局部变量来查看是否符合我们预期的执行效果。但是这些方法都有一个共同的弱点–效率低且繁琐,本文就介绍一个堪称神器的Python调试工具PySnooper,能够大大减少调试过程中的工作量。
【----帮助Python学习,以下所有学习资料文末免费领!----】
装饰器
装饰器(Decorators)是Python里一个很重要的概念,它能够使得Python代码更加简洁,用一句话概括:装饰器是修改其他函数功能的函数。PySnooper的调用主要依靠装饰器的方式,所以,了解装饰器的基本概念和使用方法更有助于理解PySnooper的使用。在这里,我先简单介绍一下装饰器的使用,如果精力有限,了解装饰器的调用方式即可。
对于Python,一切都是对象,一个函数可以作为一个对象在不同模块之间进行传递,举个例子,
def one(func):
print("now you are in function one.")
func()
def two():
print("now you are in function two")
one(two)
# 输出
>>> now you are in function one.
>>> now you are in function two.
其实这就是装饰器的核心所在,它们封装一个函数,可以用这样或那样的方式来修改它。换一种方式表达上述调用,可以用***@+函数名***来装饰一个函数。
def one(func):
print("now you are in function one.")
def warp():
func()
return warp
@one
def two():
print("now you are in function two.")
two()
# 输出
>>> now you are in function one.
>>> now you are in function two.
此外,在调用装饰器时还可以给函数传入参数:
def one(func):
print("now you are in function one.")
def warp(*args):
func(*args)
return warp
@one
def two(x, y):
print("now you are in function two.")
print("x value is %d, y value is %d" % (x, y))
two(5, 6)
# 输出
>>> now you are in function one.
>>> now you are in function two.
>>> x value is 5, y value is 6
另外,装饰器本身也可以接收参数,
def three(text):
def one(func):
print("now you are in function one.")
def warp(*args):
func(*args)
return warp
print("input params is {}".format(text))
return one
@three(text=5)
def two(x, y):
print("now you are in function two.")
print("x value is %d, y value is %d" % (x, y))
two(5, 6)
# 输出
>>> input params is 5
>>> now you are in function one.
>>> now you are in function two.
>>> x value is 5, y value is 6
上面讲述的就是Python装饰器的一些常用方法。
Pysnooper
调试程序对于大多数开发者来说是一项必不可少的工作,当我们想要知道代码是否按照预期的效果在执行时,我们会想到去输出一下局部变量与预期的进行比对。目前大多数采用的方法主要有以下几种:
- Print函数
- Log日志
- IDE调试器
但是这些方法有着无法忽视的弱点:
- 繁琐
- 过度依赖工具
“PySnooper is a poor man’s debugger.”
有了PySnooper,上述问题都迎刃而解,因为PySnooper实在太简洁了,目前在github已经10k+star。
前面花了一段篇幅讲解装饰器其实就是为了PySnooper做铺垫,PySnooper的调用就是通过装饰器的方式进行使用,非常简洁。
PySnooper的调用方式就是通过***@pysnooper.snoop***的方式进行使用,该装饰器可以传入一些参数来实现一些目的,具体如下:
参数描述None输出日志到控制台filePath输出到日志文件,例如’log/file.log’prefix给调试的行加前缀,便于识别watch查看一些非局部变量表达式的值watch_explode展开值用以查看列表/字典的所有属性或项depth显示函数调用的函数的snoop行
*安装*
使用pip安装,
pip install pysnooper
或者使用conda安装,
conda install -c conda-forge pysnooper
*使用*
先写一个简单的例子,
import numpy as np
import pysnooper
@pysnooper.snoop()
def one(number):
mat = []
while number:
mat.append(np.random.normal(0, 1))
number -= 1
return mat
one(3)
# 输出
Starting var:.. number = 3
22:17:10.634566 call 6 def one(number):
22:17:10.634566 line 7 mat = []
New var:....... mat = []
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 2
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746, -0.479901983375219]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 1
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746, -0.479901983375219, 1.0491540468063252]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 0
22:17:10.634566 line 8 while number:
22:17:10.634566 line 11 return mat
22:17:10.634566 return 11 return mat
Return value:.. [-0.4142847169210746, -0.479901983375219, 1.0491540468063252]
这是最简单的使用方法,从上述输出结果可以看出,PySnooper输出信息主要包括以下几个部分:
- 局部变量值
- 代码片段
- 局部变量所在行号
- 返回结果
也就是说,把我们日常DEBUG所需要的主要信息都输出出来了。
上述结果输出到控制台,还可以把日志输出到文件,
@pysnooper.snoop("debug.log")
在函数调用过程中,PySnooper还能够显示函数的层次关系,使得一目了然,
@pysnooper.snoop()
def two(x, y):
z = x + y
return z
@pysnooper.snoop()
def one(number):
k = 0
while number:
k = two(k, number)
number -= 1
return number
one(3)
# 输出
Starting var:.. number = 3
22:26:08.185204 call 12 def one(number):
22:26:08.186202 line 13 k = 0
New var:....... k = 0
22:26:08.186202 line 14 while number:
22:26:08.186202 line 15 k = two(k, number)
Starting var:.. x = 0
Starting var:.. y = 3
22:26:08.186202 call 6 def two(x, y):
22:26:08.186202 line 7 z = x + y
New var:....... z = 3
22:26:08.186202 line 8 return z
22:26:08.186202 return 8 return z
Return value:.. 3
Modified var:.. k = 3
22:26:08.186202 line 16 number -= 1
Modified var:.. number = 2
22:26:08.186202 line 14 while number:
22:26:08.186202 line 15 k = two(k, number)
Starting var:.. x = 3
Starting var:.. y = 2
22:26:08.186202 call 6 def two(x, y):
22:26:08.186202 line 7 z = x + y
New var:....... z = 5
22:26:08.186202 line 8 return z
22:26:08.186202 return 8 return z
Return value:.. 5
Modified var:.. k = 5
22:26:08.186202 line 16 number -= 1
Modified var:.. number = 1
22:26:08.186202 line 14 while number:
22:26:08.186202 line 15 k = two(k, number)
Starting var:.. x = 5
Starting var:.. y = 1
22:26:08.186202 call 6 def two(x, y):
22:26:08.186202 line 7 z = x + y
New var:....... z = 6
22:26:08.186202 line 8 return z
22:26:08.186202 return 8 return z
Return value:.. 6
Modified var:.. k = 6
22:26:08.186202 line 16 number -= 1
Modified var:.. number = 0
22:26:08.186202 line 14 while number:
22:26:08.186202 line 17 return number
22:26:08.186202 return 17 return number
Return value:.. 0
从上述输出结果可以看出,函数one与函数two的输出缩进层次一目了然。
除了缩进之外,PySnooper还提供了参数***prefix***给debug信息添加前缀的方式便于识别,
@pysnooper.snoop(prefix="funcTwo ")
def two(x, y):
z = x + y
return z
@pysnooper.snoop(prefix="funcOne ")
def one(number):
k = 0
while number:
k = two(k, number)
number -= 1
return number
one(3)
# 输出
funcOne Starting var:.. number = 3
funcOne 22:28:14.259212 call 12 def one(number):
funcOne 22:28:14.260211 line 13 k = 0
funcOne New var:....... k = 0
funcOne 22:28:14.260211 line 14 while number:
funcOne 22:28:14.260211 line 15 k = two(k, number)
funcTwo Starting var:.. x = 0
funcTwo Starting var:.. y = 3
funcTwo 22:28:14.260211 call 6 def two(x, y):
funcTwo 22:28:14.260211 line 7 z = x + y
funcTwo New var:....... z = 3
funcTwo 22:28:14.260211 line 8 return z
funcTwo 22:28:14.260211 return 8 return z
funcTwo Return value:.. 3
funcOne Modified var:.. k = 3
funcOne 22:28:14.260211 line 16 number -= 1
funcOne Modified var:.. number = 2
funcOne 22:28:14.260211 line 14 while number:
funcOne 22:28:14.260211 line 15 k = two(k, number)
funcTwo Starting var:.. x = 3
funcTwo Starting var:.. y = 2
funcTwo 22:28:14.260211 call 6 def two(x, y):
funcTwo 22:28:14.260211 line 7 z = x + y
funcTwo New var:....... z = 5
funcTwo 22:28:14.260211 line 8 return z
funcTwo 22:28:14.260211 return 8 return z
funcTwo Return value:.. 5
funcOne Modified var:.. k = 5
funcOne 22:28:14.260211 line 16 number -= 1
funcOne Modified var:.. number = 1
funcOne 22:28:14.260211 line 14 while number:
funcOne 22:28:14.260211 line 15 k = two(k, number)
funcTwo Starting var:.. x = 5
funcTwo Starting var:.. y = 1
funcTwo 22:28:14.260211 call 6 def two(x, y):
funcTwo 22:28:14.260211 line 7 z = x + y
funcTwo New var:....... z = 6
funcTwo 22:28:14.260211 line 8 return z
funcTwo 22:28:14.260211 return 8 return z
funcTwo Return value:.. 6
funcOne Modified var:.. k = 6
funcOne 22:28:14.260211 line 16 number -= 1
funcOne Modified var:.. number = 0
funcOne 22:28:14.260211 line 14 while number:
funcOne 22:28:14.260211 line 17 return number
funcOne 22:28:14.260211 return 17 return number
funcOne Return value:.. 0
参数***watch***可以用于查看一些非局部变量,例如,
class Test():
t = 10
test = Test()
@pysnooper.snoop(watch=("test.t", "x"))
# 输出
Starting var:.. number = 3
Starting var:.. test.t = 10
Starting var:.. x = 10
参数 watch_explode 可以展开字典或者列表显示它的所有属性值,对比一下它和 watch 的区别,
#### watch_explode ####
d = {
"one": 1,
"two": 1
}
@pysnooper.snoop(watch_explode="d")
# 输出
Starting var:.. number = 3
Starting var:.. d = {'one': 1, 'two': 1}
Starting var:.. d['one'] = 1
Starting var:.. d['two'] = 1
#### watch ####
d = {
"one": 1,
"two": 1
}
@pysnooper.snoop(watch="d")
# 输出
Starting var:.. d = {'one': 1, 'two': 1}
可以看出 watch_explode能够展开字典的属性值。
另外还有参数 depth显示函数中调用函数的snoop行,默认值为1,参数值需要大于或等于1。
当然,本文介绍的只是冰山一角。
在庞大的Python知识体系中,还有很多「绝妙」的奇技淫巧能够极大的提高开发效率,提升代码质量和稳定性。
读者福利:知道你对Python感兴趣,便准备了这套python学习资料
对于0基础小白入门:
如果你是零基础小白,想快速入门Python是可以考虑的。
一方面是学习时间相对较短,学习内容更全面更集中。
二方面是可以根据这些资料规划好学习计划和方向。
包括:Python激活码+安装包、Python web开发,Python爬虫,Python数据分析,人工智能、机器学习等习教程。带你从零基础系统性的学好Python!
零基础Python学习资源介绍
① Python所有方向的学习路线图,清楚各个方向要学什么东西
② 600多节Python课程视频,涵盖必备基础、爬虫和数据分析
③ 100多个Python实战案例,含50个超大型项目详解,学习不再是只会理论
④ 20款主流手游迫解 爬虫手游逆行迫解教程包
⑤ 爬虫与反爬虫攻防教程包,含15个大型网站迫解
⑥ 爬虫APP逆向实战教程包,含45项绝密技术详解
⑦ 超300本Python电子好书,从入门到高阶应有尽有
⑧ 华为出品独家Python漫画教程,手机也能学习
⑨ 历年互联网企业Python面试真题,复习时非常方便
👉Python学习路线汇总👈
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。(全套教程文末领取哈)
👉Python必备开发工具👈
温馨提示:篇幅有限,已打包文件夹,获取方式在:文末
👉Python学习视频600合集👈
观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
👉实战案例👈
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
👉100道Python练习题👈
检查学习结果。
👉面试刷题👈
资料领取
上述这份完整版的Python全套学习资料已经上传CSDN官方,朋友们如果需要可以微信扫描下方CSDN官方认证二维码 即可领取↓↓↓
好文推荐
了解python的前景:https://blog.csdn.net/SpringJavaMyBatis/article/details/127194835
了解python的兼职副业:https://blog.csdn.net/SpringJavaMyBatis/article/details/127196603