使用這個(gè)開源工具來監(jiān)控 Python 中的變量
Watchpoints 是一個(gè)簡單但功能強(qiáng)大的工具,可以幫助你在調(diào)試 Python 時(shí)監(jiān)控變量。
在調(diào)試代碼時(shí),你經(jīng)常面臨著要弄清楚一個(gè)變量何時(shí)發(fā)生變化。如果沒有任何高級(jí)工具,那么可以選擇使用打印語句在期望它們更改時(shí)輸出變量。然而,這是一種非常低效的方法,因?yàn)樽兞靠赡茉诤芏嗟胤桨l(fā)生變化,并且不斷地將其打印到終端上會(huì)產(chǎn)生很大的干擾,而將它們打印到日志文件中則變得很麻煩。
這是一個(gè)常見的問題,但現(xiàn)在有一個(gè)簡單而強(qiáng)大的工具可以幫助你監(jiān)控變量:watchpoints。
“監(jiān)視點(diǎn)”的概念在 C 和 C++ 調(diào)試器中很常見,用于監(jiān)控內(nèi)存,但在 Python 中缺乏相應(yīng)的工具。watchpoints
填補(bǔ)了這個(gè)空白。
安裝
要使用它,你必須先用 pip
安裝它:
$ python3 -m pip install watchpoints
在Python中使用 watchpoints
對(duì)于任何一個(gè)你想監(jiān)控的變量,使用 watch
函數(shù)對(duì)其進(jìn)行監(jiān)控。
from watchpoints import watch
a = 0
watch(a)
a = 1
當(dāng)變量發(fā)生變化時(shí),它的值就會(huì)被打印到標(biāo)準(zhǔn)輸出:
====== Watchpoints Triggered ======
Call Stack (most recent call last):
<module> (my_script.py:5):
> a = 1
a:
0
->
1
信息包括:
- 變量被改變的行。
- 調(diào)用棧。
- 變量的先前值/當(dāng)前值。
它不僅適用于變量本身,也適用于對(duì)象的變化:
from watchpoints import watch
a = []
watch(a)
a = {} # 觸發(fā)
a["a"] = 2 # 觸發(fā)
當(dāng)變量 a
被重新分配時(shí),回調(diào)會(huì)被觸發(fā),同時(shí)當(dāng)分配給 a
的對(duì)象發(fā)生變化時(shí)也會(huì)被觸發(fā)。
更有趣的是,監(jiān)控不受作用域的限制。你可以在任何地方觀察變量/對(duì)象,而且無論程序在執(zhí)行什么函數(shù),回調(diào)都會(huì)被觸發(fā)。
from watchpoints import watch
def func(var):
var["a"] = 1
a = {}
watch(a)
func(a)
例如,這段代碼打印出:
====== Watchpoints Triggered ======
Call Stack (most recent call last):
<module> (my_script.py:8):
> func(a)
func (my_script.py:4):
> var["a"] = 1
a:
{}
->
{'a': 1}
watch
函數(shù)不僅可以監(jiān)視一個(gè)變量,它也可以監(jiān)視一個(gè)字典或列表的屬性和元素。
from watchpoints import watch
class MyObj:
def __init__(self):
self.a = 0
obj = MyObj()
d = {"a": 0}
watch(obj.a, d["a"]) # 是的,你可以這樣做
obj.a = 1 # 觸發(fā)
d["a"] = 1 # 觸發(fā)
這可以幫助你縮小到一些你感興趣的特定對(duì)象。
如果你對(duì)輸出格式不滿意,你可以自定義它。只需定義你自己的回調(diào)函數(shù):
watch(a, callback=my_callback)
# 或者全局設(shè)置
watch.config(callback=my_callback)
當(dāng)觸發(fā)時(shí),你甚至可以使用 pdb
:
watch.config(pdb=True)
這與 breakpoint()
的行為類似,會(huì)給你帶來類似調(diào)試器的體驗(yàn)。
如果你不想在每個(gè)文件中都導(dǎo)入這個(gè)函數(shù),你可以通過 install
函數(shù)使其成為全局:
watch.install() # 或 watch.install("func_name") ,然后以 func_name() 方式使用
我個(gè)人認(rèn)為,watchpoints
最酷的地方就是使用直觀。你對(duì)一些數(shù)據(jù)感興趣嗎?只要“觀察”它,你就會(huì)知道你的變量何時(shí)發(fā)生變化。
嘗試 watchpoints
我在 GitHub 上開發(fā)維護(hù)了 watchpoints
,并在 Apache 2.0 許可下發(fā)布了它。安裝并使用它,當(dāng)然也歡迎大家做出貢獻(xiàn)。