说实话,读源码这事,谁干谁知道。一行一行看,变量满天飞,函数跳来跳去,看得脑壳疼。你能坚持看下来已经很了不起了。我今天不跟你讲什么大道理,就聊点真能帮上忙的东西。
PyCharm里有个东西叫“调试器”,大部分人只用它来找bug。其实只要把调试器用好,看源码跟看自己写的代码一样清楚。
你肯定遇到过这种情况:一个开源项目,你点进一个函数,发现它调了另一个函数,那个函数又调了另一个。看半天,整个调用链全是碎片,连变量值是什么都不知道。这时候别硬看,直接上调试器跑一遍。
具体做法很简单。在你怀疑的那行代码左侧点一下,出现红色圆点。然后按Shift+F9启动调试。程序会停在你点的那行。这时候你可以按F8一步步往下走,每走一步,右边Variables窗口里所有变量值都会变。你看着变量从无到有,从整数变字符串,从None变成一个对象,整个过程跟看电影一样清楚。
最实用的是Step Into按钮(F7)。当你看到一行代码调用了某个函数,按F7就直接跳进那个函数的内部。你会看到它怎么接收参数,怎么处理,怎么返回。再也不用靠猜了。想跳出来就按Shift+F8。
还有个功能很多人不知道。调试器底下有个Console窗口,这个窗口是活的。程序运行到断点停住的时候,你可以在Console里直接输入表达式。比如你看到变量名叫user_list,但不确定它里面是什么,直接打len(user_list)看长度,或者打type(user_list)看类型。这比翻代码快得多。
我以前看Django的源码,一个请求进来,中间件、视图函数、模板渲染,路径绕来绕去。我直接在请求入口打一个断点,然后一步一步跟着走。走到某个地方发现有个get_response函数,我以为它很普通,进去一看,里面竟然调了异常处理和上下文管理器。当场就明白了Django的请求生命周期到底怎么回事。
你真正想提升能力,不是靠背源码,是靠跑源码。
有些代码逻辑藏得特别深。比如设计模式里的策略模式,你看到接口调了一个方法,实际上具体实现可能在好几个子类里。硬读你会疯掉。调试器一跑,实际跑的是哪个类,变量里存的是什么类型,一眼就看到了。
有同事问我,看源码总卡在递归函数上。递归函数最怕绕进去出不来。我教他这么干:在递归函数的开头打一个断点,然后在Variables里看每次递归传入的参数。你会发现递归其实不神秘,就是参数越来越小,直到满足条件返回。调试器帮你把这个过程变成可视的。
别怕把代码跑乱了。开调试器不影响你正常写代码。你可以在项目里新建一个测试文件,导入你要研究的模块,写几行最简单的调用代码。然后打断点,跑起来。这时候PyCharm会按照你的测试代码走,你只关心你想看的那部分逻辑就行了。
有些人觉得看源码一定要自己捋清楚每行字。这是错的。你不需要记住所有细节。你只需要抓住数据流和控制流。数据流就是变量怎么变,控制流就是代码怎么跳。调试器同时展示这两样东西。
有一次我看一个流式计算框架的源码。里面有个map操作,调了一个叫compute的东西。我点进去一看,又调了internalCompute。再点进去,里面有个doCompute。我差点把电脑砸了。后来我直接在compute那行打断点,F7进去,进去之后在internalCompute那行也打断点,再F7。就这样几层打下来,我发现它其实只是做了个函数链调用,真正处理数据的是最里层那个doCompute。这种嵌套结构,不跑你永远猜不到。
调试器还有一个隐藏用法。你可以随时修改变量的值。在Variables窗口里,双击一个变量的值,改成你想要的。比如一个参数是age=25,你改成age=50,看看代码会怎么处理。这能帮你测试边界情况和异常分支,不用每次改代码重启。
我建议你从今天开始,不要再抱着源码干啃了。打开你手头用的框架或者库,找到你最困惑的一个模块,在PyCharm里建个测试文件,导入它,打上断点,按Shift+F9。跟着变量走一遍,你会有种豁然开朗的感觉。
调试器是你最好的老师。它会告诉你代码到底怎么跑,而不是你怎么想象它跑。每多调试一次,你对程序的理解就深一层。用不了几个月,你再看那些“高深”的源码,会觉得它们也就那么回事。
夜雨聆风