1. 快速打印函数的调用栈
在阅读源码的时候,有时候我们想要看整个程序的调用栈是怎样的?
比较常规的做法是使用全局搜索函数,一层一层往上走,效率非常之低。
而我习惯了使用 pdb 对代码进行调试,在使用pdb时,可以使用如下代码打出调用栈
import traceback
traceback.print_stack(file=sys.stdout)
或者直接使用 where
(更简单的直接一个 w
)把整个函数的调用栈给打印出来
(Pdb) where
/usr/lib/python2.7/site-packages/eventlet/greenpool.py(82)_spawn_n_impl()
-> func(*args, **kwargs)
/usr/lib/python2.7/site-packages/eventlet/wsgi.py(719)process_request()
-> proto.__init__(sock, address, self)
/usr/lib64/python2.7/SocketServer.py(649)__init__()
-> self.handle()
/usr/lib64/python2.7/BaseHTTPServer.py(340)handle()
-> self.handle_one_request()
/usr/lib/python2.7/site-packages/eventlet/wsgi.py(384)handle_one_request()
-> self.handle_one_response()
/usr/lib/python2.7/site-packages/eventlet/wsgi.py(481)handle_one_response()
2. 如何快速编译 Python 脚本?
pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code,py文件变成pyc文件后,加载的速度会有所提高。因此在一些场景下,可以预先编译成 pyc 文件,来提高加载速度。
编译的命令非常的简单,示例如下
$ tree demo
demo
└── main.py
$ python3 -O -m compileall demo
Listing 'demo'...
Compiling 'demo/main.py'...
$ tree demo
demo
├── __pycache__
│ └── main.cpython-39.opt-1.pyc
└── main.py
3. 交互模式下下划线的秒用
对于 _
,大家对于他的印象都是用于占位符,省得为一个不需要用到的变量,绞尽脑汁的想变量名。
今天要介绍的是他的第二种用法,就是在交互式模式下的应用。
示例如下:
>>> 3 + 4
7
>>> _
7
>>> name='iswbm'
>>> name
'iswbm'
>>> _
'iswbm'
它记录的是上一次表达式中返回的 非 None 值。
一定要是非 None值 ,否则 _ 存储的值不会被更新。这就是为什么print函数打印出来的值不会被存储。
>>> 3 + 4
7
>>> _
7
>>> print("iswbm")
iswbm
>>> _
7
因为 print 函数打印的内容并不是函数本身的返回值,print 函数返回的 None
4. 让脚本报错后立即进入调试模式
当你在使用 python xxx.py
这样的方法,执行 Python 脚本时,若因为代码 bug 导致异常未捕获,那整个程序便会终止退出。
这个时候,我们通常会去排查是什么原因导致的程序崩溃。
大家都知道,排查问题的思路,第一步肯定是去查看日志,若这个 bug 隐藏的比较深,只有在特定场景下才会现身,那么还需要开发者,复现这个 bug,方能优化代码。
复现有时候很难,有时候虽然简单,但是要伪造各种数据,相当麻烦。
如果有一种方法能在程序崩溃后,立马进入调试模式该有多好啊?
既然都这么问了,那肯定是带着解决方案来的。
只要你在执行脚本行,加上 -i
参数,即可在脚本执行完毕后进入 Python Shell 模式,方便你进行调试。
具体演示如下:
需要注意的是:脚本执行完毕,有两种情况:
- 正常退出
- 异常退出
这两种都会进入 Python Shell,如果脚本并无异常,最终也会进入 Python Shell 模式,需要你手动退出
如果希望脚本正确完成时自动推出,可以在脚本最后加上一行__import__("os")._exit(0)
5. 快速将项目打包成应用程序
假设我当前有一个 demo 项目,目录结构树及相关文件的的代码如下
现在我使用如下命令,将该项目进行打包,其中 demo 是项目的文件夹名,main:main
中的第一个 main 指的 main.py
,而第二个 main 指的是 main
函数
$ python3 -m zipapp demo -m "main:main"
执行完成后,会生成一个 demo.pyz
文件,可直接执行它。
具体演示过程如下
6. 往 Python Shell 中传入参数
往一个 Python 脚本传入参数,是一件非常简单的事情。
比如这样:
$ python demo.py arg1 arg2
我在验证一些简单的 Python 代码时,喜欢使用 Python Shell 。
那有没有办法在使用 Python Shell 时,向上面传递参数一样,传入参数呢?
经过我的摸索,终于找到了方法,具体方法如下:
7. 最快查看包搜索路径的方式
当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path
查看。
>>> import sys
>>> from pprint import pprint
>>> pprint(sys.path)
['',
'/usr/local/Python3.7/lib/python37.zip',
'/usr/local/Python3.7/lib/python3.7',
'/usr/local/Python3.7/lib/python3.7/lib-dynload',
'/home/wangbm/.local/lib/python3.7/site-packages',
'/usr/local/Python3.7/lib/python3.7/site-packages']
>>>
那有没有更快的方式呢?
我这有一种连 console 模式都不用进入的方法呢?
你可能会想到这种,但这本质上与上面并无区别
[wangbm@localhost ~]$ python -c "print('\n'.join(__import__('sys').path))"
/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg
/usr/lib/python2.7/site-packages/redis-3.0.1-py2.7.egg
/usr/lib64/python27.zip
/usr/lib64/python2.7
/usr/lib64/python2.7/plat-linux2
/usr/lib64/python2.7/lib-tk
/usr/lib64/python2.7/lib-old
/usr/lib64/python2.7/lib-dynload
/home/wangbm/.local/lib/python2.7/site-packages
/usr/lib64/python2.7/site-packages
/usr/lib64/python2.7/site-packages/gtk-2.0
/usr/lib/python2.7/site-packages
这里我要介绍的是比上面两种都方便的多的方法,一行命令即可解决
[wangbm@localhost ~]$ python3 -m site
sys.path = [
'/home/wangbm',
'/usr/local/Python3.7/lib/python37.zip',
'/usr/local/Python3.7/lib/python3.7',
'/usr/local/Python3.7/lib/python3.7/lib-dynload',
'/home/wangbm/.local/lib/python3.7/site-packages',
'/usr/local/Python3.7/lib/python3.7/site-packages',
]
USER_BASE: '/home/wangbm/.local' (exists)
USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists)
ENABLE_USER_SITE: True
从输出你可以发现,这个列的路径会比 sys.path
更全,它包含了用户环境的目录。