python pdb調試
python -m pdb myscript.py #注意這會重啟myscript.py
可以在程序中這麼設置斷點:
import pdb; pdb.set_trace()
可以修改變量的值,但是要注意,前面加上!比如要修改final的值,應該這樣!final="newvalue"
支持的命令:
p 打印變量
n next
step 細點運行
c continue
l list
a args 打印當前函數的參數
condition bpnumber [condition]
clear/disable/enable 清除/禁用/使能斷點
q quit
python profiler性能分析
一種方法:
if __name__ == "__main__":
import profile
profile.run("foo()")
另一種命令行方法:
python -m profile prof1.py
profile的統計結果分為ncalls, tottime, percall, cumtime, percall, filename:lineno(function)等若干列:
ncalls
函數的被調用次數
tottime
函數總計運行時間,除去函數中調用的函數運行時間
percall
函數運行一次的平均時間,等於tottime/ncalls
cumtime
函數總計運行時間,含調用的函數運行時間
percall
函數運行一次的平均時間,等於cumtime/ncalls
filename:lineno(function)
函數所在的文件名,函數的行號,函數名
用pstats自定義報表
profile解 決了我們的一個需求,還有一個需求:以多種形式查看輸出,我們可以通過 profile的另一個類Stats來解決。在這裡我們需要引入一個模塊pstats,它定義了一個類Stats,Stats的構造函數接受一個參數—— 就是profile的輸出文件的文件名。Stats提供了對profile輸出結果進行排序、輸出控制等功能,如我們把前文的程序改為如下:
# …略
if __name__ == "__main__":
import profile
profile.run("foo()", "prof.txt")
import pstats
p = pstats.Stats("prof.txt")
p.sort_stats("time").print_stats()
引入pstats之後,將profile的輸出按函數占用的時間排序
Stats有若干個函數,這些函數組合能給我們輸出不同的profile報表,功能非常強大。下面簡單地介紹一下這些函數:
strip_dirs()
用以除去文件名前名的路徑信息。
add(filename,[…])
把profile的輸出文件加入Stats實例中統計
dump_stats(filename)
把Stats的統計結果保存到文件
sort_stats(key,[…])
最重要的一個函數,用以排序profile的輸出
reverse_order()
把Stats實例裡的數據反序重排
print_stats([restriction,…])
把Stats報表輸出到stdout
print_callers([restriction,…])
輸出調用了指定的函數的函數的相關信息
print_callees([restriction,…])
輸出指定的函數調用過的函數的相關信息
這裡最重要的函數就是sort_stats和print_stats,通過這兩個函數我們幾乎可以用適當的形式浏覽所有的信息了,下面來詳細介紹一下。
sort_stats() 接受一個或者多個字符串參數,如”time”、”name” 等,表明要根據哪一列來排序,這相當有用,例如我們可以通過用time為key來排序得知最消耗時間的函數,也可以通過cumtime來排序,獲知總消耗 時間最多的函數,這樣我們優化的時候就有了針對性,也就事半功倍了。sort_stats可接受的參數如下:
‘ncalls’
被調用次數
‘cumulative’
函數運行的總時間
‘file’
文件名
‘module’
文件名
‘pcalls’
簡單調用統計(兼容舊版,未統計遞歸調用)
‘line’
行號
‘name’
函數名
‘nfl’
Name/file/line
‘stdname’
標准函數名
‘time’
函數內部運行時間(不計調用子函數的時間)
另一個相當重要的函數就是print_stats——用以根據最後一次調用sort_stats之後得到的報表。
cProfile
python -m cProfile -s time test.py
timeit
如果我們某天心血來潮,想要向list裡append一個元素需要多少時間或者想知道拋出一個異常要多少時間,那使用profile就好像用牛刀殺雞了。這時候我們更好的選擇是timeit模塊。
timeit除了有非常友好的編程接口,也同樣提供了友好的命令行接口。首先來看看編程接口。timeit模塊包含一個類Timer,它的構造函數是這樣的:
class Timer( [stmt='pass' [, setup='pass' [, timer=<timer function>]]])
stmt參數是字符串形式的一個代碼段,這個代碼段將被評測運行時間;setup參數用以設置stmt的運行環境;timer可以由用戶使用自定義精度的計時函數。
timeit.Timer有三個成員函數,下面簡單介紹一下:
timeit( [number=1000000])
timeit()執行一次Timer構造函數中的setup語句之後,就重復執行number次stmt語句,然後返回總計運行消耗的時間。
repeat( [repeat=3 [, number=1000000]])
repeat()函數以number為參數調用timeit函數repeat次,並返回總計運行消耗的時間
print_exc( [file=None])
print_exc()函數用以代替標准的tracback,原因在於print_exc()會輸出錯行的源代碼,如:
>>> t = timeit.Timer("t = foo()/nprint t") ß被timeit的代碼段
>>> t.timeit()
Traceback (most recent call last):
File "<pyshell#12>", line 1, in -toplevel-
t.timeit()
File "E:/Python23/lib/timeit.py", line 158, in timeit
return self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
foo() ß標准輸出是這樣的
NameError: global name 'foo' is not defined
>>> try:
t.timeit()
except:
t.print_exc()
Traceback (most recent call last):
File "<pyshell#17>", line 2, in ?
File "E:/Python23/lib/timeit.py", line 158, in timeit
return self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
t = foo() ßprint_exc()的輸出是這樣的,方便定位錯誤
NameError: global name 'foo' is not defined
除了可以使用timeit的編程接口外,我們也可以在命令行裡使用timeit,非常方便:
python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement ...]
其中參數的定義如下:
-n N/--number=N
statement語句執行的次數
-r N/--repeat=N
重復多少次調用timeit(),默認為3
-s S/--setup=S
用以設置statement執行環境的語句,默認為”pass”
-t/--time
計時函數,除了Windows平台外默認使用time.time()函數,
-c/--clock
計時函數,Windows平台默認使用time.clock()函數
-v/--verbose
輸出更大精度的計時數值
-h/--help
簡單的使用幫助