精通Python自動化腳本-第二章 Python腳本調(diào)試和性能測試

精通Python自動化腳本-運維人員寶典完整目錄:

第一章 Python腳本概述
第二章 Python腳本調(diào)試和性能測試
第三章 單元測試-單元測試框架的介紹
第四章 自動化常規(guī)運維活動
第五章 文件罐盔、目錄和數(shù)據(jù)處理
第六章 文件存檔彩扔、加密和解密
第七章 文本處理和正則表達式
第八章 文檔和報告
第九章 操作各類文件
第十章 網(wǎng)絡(luò)基礎(chǔ) - Socket編程
第十一章 使用Python腳本處理郵件
第十二章 使用Telnet和SSH遠程監(jiān)控主機
第十三章 創(chuàng)建圖形化用戶界面
第十四章 處理Apache和其它的日志文件
第十五章 SOAP和REST API通訊
第十六章 網(wǎng)絡(luò)抓取 - 從網(wǎng)站上提取有用的信息
第十七章 數(shù)據(jù)收集及報表
第十八章 MySQL和SQLite數(shù)據(jù)庫管理

什么是調(diào)試铺浇?

調(diào)試(debugging)是一個解決代碼中錯誤或?qū)е萝浖荒苷_\行的問題的過程。Python中的調(diào)試非常容易。Python調(diào)試器設(shè)置條件斷點并對源碼逐行調(diào)試确虱。我們將使用Python標(biāo)準(zhǔn)庫中的 pdb 模塊來對我們的Python腳本進行調(diào)試隘世。

Python 的調(diào)試技術(shù)

為更好的調(diào)試Python程序,可以使用不同的技術(shù)怯邪。我們就來看看Python調(diào)試的四種技術(shù):

  • print()語句:這是了解具體發(fā)生情況的最簡單的方式绊寻,這樣我們可以檢查執(zhí)行的內(nèi)容
  • logging:這類似于print語句但帶更多的上下文信息,因此我們可以更全面的了解情況
  • pdb調(diào)試器:這是最常使用的調(diào)試技術(shù)悬秉。使用 pdb 的優(yōu)勢是能夠在命令行澄步、解釋器以及程序中使用 pdb
  • IDE調(diào)試器:IDE有內(nèi)置的調(diào)試器。這讓開發(fā)者可以執(zhí)行自己的代碼和泌,然后開發(fā)者可以在程序執(zhí)行過程中檢查代碼

錯誤處理(異常處理)

在這一部分中我們將學(xué)習(xí)Python如何處理異常村缸。但首先什么是異常呢?異常是在程序執(zhí)行過程中發(fā)生的錯誤武氓。每當(dāng)錯誤發(fā)生時梯皿,Python會生成一個異常,使用try…except代碼塊來進行處理县恕。有時異常程序無法處理东羹,因此會導(dǎo)致報錯信息。下面我們就來看一些異常的示例:

在你的終端中忠烛,啟動python3交互控制臺属提,我們一起來看一些異常示例:

>>> 50 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 6 + abc*5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'abc' is not defined
>>> 'abc' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly
>>> import abcd
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'abcd'

這就是異常的一些示例。下面我們來看如何處理這些異常美尸。

每當(dāng)Python程序中發(fā)生錯誤時冤议,就會拋出異常。我們也可使用raise關(guān)鍵字來強制拋出異常师坎。

下來我們來看一個處理異常的try…except代碼塊恕酸。在try代碼塊中,我們將編寫可能生成異常的代碼胯陋。在except代碼塊中蕊温,我們將編寫異常的處理方式。try…except的語法如下:

try:
        statement(s)
except:
        statement(s)

一個try代碼塊可帶有多個except語句惶岭。我們可通過在except關(guān)鍵字之后輸入異常的名稱來處理指定的異常寿弱。處理指定異常的語法如下:

try:
        statement(s)
except exception_name:
        statement(s)

下面我們創(chuàng)建一個exception_example.py腳本來捕獲ZeroDivisionError。在腳本中編寫如下代碼:

a = 35
b = 37
try:
        c = a +b
        print("The value of c is:", c)
        d = b / 0
        print("The value of d is:", d)
except:
        print("Division by zero is not possible")
print("Out of try...except block")

像下面這樣運行腳本按灶,將會得到如下結(jié)果:

vagrant@python-scripting:~$ python3 exception_example.py
The value of c is: 72
Division by zero is not possible
Out of try...except block

調(diào)試器工具

Python中支持很多種調(diào)試工具:

  • winpdb
  • pydev
  • pydb
  • pdb
  • gdb
  • pyDebug

這一部分中症革,我們將學(xué)習(xí)pdb Python調(diào)試器。pdb是Python標(biāo)準(zhǔn)庫的一部分并一直可以直接使用鸯旁。

pdb調(diào)試器

pdbs模塊用于調(diào)試Python程序噪矛。Python程序使用pdb交互源代碼調(diào)試器來調(diào)試程序量蕊。pdb設(shè)置斷點交檢查棧幀,列出源代碼艇挨。

下面我們將學(xué)習(xí)如何使用pdb調(diào)試器残炮。使用這一調(diào)試器有三種方式:

  • 在解釋器之中
  • 通過命令行
  • 在Python腳本中

我們將創(chuàng)建一個pdb_example.py腳本并在該腳本中添加如下內(nèi)容:

class Student:
        def __init__(self, std):
                self.count = std

        def print_std(self):
                for i in range(self.count):
                        print(i)
                return

if __name__ == "__main__":
        Student(5).print_std()

使用這一腳本作為學(xué)習(xí)Python調(diào)試的示例,我們將了解如何啟動調(diào)試器的細節(jié)缩滨。

解釋器內(nèi)調(diào)試

要從Python交互控制臺中啟動調(diào)試器势就,我們使用run()或runeval()。

啟動python3交互控制臺脉漏。運行如下命令來啟動控制臺:

$ python3

導(dǎo)入我們的pdb_example腳本名和pdb模塊苞冯。下面我們將使用run(),并且我們會傳入一個字符串表達式來作為run()的參數(shù)侧巨,由Python解釋器自身進行運行:

>>> import pdb_example
>>> import pdb
>>> pdb.run('pdb_example.Student(5).print_std()')
> <string>(1)<module>()
(Pdb)

要繼續(xù)調(diào)試舅锄,在(Pdb)提示符之后輸入continue并按下Enter。我果想要了解這里可以使用的選項司忱,在(Pdb)提示符之后按下兩次Tab鍵皇忿。

在輸入continue之后,我們將得到如下的輸出:

>>> import pdb_example
>>> import pdb
>>> pdb.run('pdb_example.Student(5).print_std()')
> <string>(1)<module>()
(Pdb) continue
0
1
2
3
4
>>>

命令行調(diào)試

運行調(diào)試器最簡單也最直接的方式是通過命令行坦仍。我們的程序?qū)⒆鳛檎{(diào)試器的輸入鳍烁。我們可以這樣在命令行中使用調(diào)試器:

$ python3 -m pdb pdb_example.py

在從命令行運行調(diào)試器時,源代碼會被載入并在調(diào)試器找到的第一行停止執(zhí)行繁扎。輸入continue來繼續(xù)調(diào)試老翘。輸出如下:

vagrant@python-scripting:~$ python3 -m pdb pdb_example.py
> /home/vagrant/pdb_example.py(1)<module>()
-> class Student:
(Pdb) continue
0
1
2
3
4
The program finished and will be restarted
> /home/vagrant/pdb_example.py(1)<module>()
-> class Student:
(Pdb)

Python腳本內(nèi)調(diào)試

以上兩種技術(shù)會在Python程序開始時啟動調(diào)試器。但第三種方法對于長期處理來說最佳锻离。要在腳本中啟動調(diào)試器,使用set_trace()墓怀。

現(xiàn)在修改pdb_example.py文件如下:

import pdb

class Student:
        def __init__(self, std):
                self.count = std

        def print_std(self):
                for i in range(self.count):
                        pdb.set_trace()
                        print(i)
                return

if __name__ == "__main__":
        Student(5).print_std()

現(xiàn)在運行程序如下:

vagrant@python-scripting:~$ python3 pdb_example.py
> /home/vagrant/pdb_example.py(10)print_std()
-> print(i)
(Pdb) continue
0
> /home/vagrant/pdb_example.py(9)print_std()
-> pdb.set_trace()
(Pdb)

set_trace()是一個Python函數(shù)汽纠,因此可以在程序的任意處調(diào)用它。所以我們有三種方式來啟動調(diào)試器傀履。

基本程序崩潰調(diào)試

在這一部分中虱朵,我們來看看trace模塊。trace模塊有助于追蹤程序的執(zhí)行钓账。因此不論何時程序崩潰碴犬,我們都能了解在哪里出現(xiàn)的崩潰。我們可以在腳本中導(dǎo)入也可以通過命令行來使用trace模塊梆暮。

現(xiàn)在我們將創(chuàng)建一個名為trace_example.py的腳本并在該腳本中編寫如下代碼:

class Student:
        def __init__(self, std):
                self.count = std

        def go(self):
                for i in range(self.count):
                        print(i)
                return

if __name__ == "__main__":
        Student(5).go()

輸出如下:

>vagrant@python-scripting:~$ python3 -m trace --trace trace_example.py
 --- modulename: trace_example, funcname: <module>
trace_example.py(1): class Student:
 --- modulename: trace_example, funcname: Student
trace_example.py(1): class Student:
trace_example.py(2):    def __init__(self, std):
trace_example.py(5):    def go(self):
trace_example.py(10): if __name__ == "__main__":
trace_example.py(11):   Student(5).go()
 --- modulename: trace_example, funcname: __init__
trace_example.py(3):        self.count = std
 --- modulename: trace_example, funcname: go
trace_example.py(6):        for i in range(self.count):
trace_example.py(7):            print(i)
0
trace_example.py(6):        for i in range(self.count):
trace_example.py(7):            print(i)
1
trace_example.py(6):        for i in range(self.count):
trace_example.py(7):            print(i)
2
trace_example.py(6):        for i in range(self.count):
trace_example.py(7):            print(i)
3
trace_example.py(6):        for i in range(self.count):
trace_example.py(7):            print(i)
4
trace_example.py(6):        for i in range(self.count):
trace_example.py(8):        return
 --- modulename: trace, funcname: _unsettrace
trace.py(77):         sys.settrace(None)

因此通過在命令行中使用trace --trace服协,開發(fā)人員可以對程序逐行追蹤。這樣在程序崩潰時啦粹,開發(fā)人員就會知道發(fā)生崩潰的實例偿荷。

程序性能和時耗分析

對Python程序進行性能分析(profiling)表示度量程序的執(zhí)行時間窘游。它計量每個函數(shù)所花的時間。Python的cProfile模塊用于對Python程序進行性能分析跳纳。

cProfile模塊

正如前文所講到的忍饰,性能分析表示度量程序的執(zhí)行時間。我們就來使用cProfile Python模塊對程序進行性能分析寺庄。

現(xiàn)在來編寫一個cprof_example.py腳本并加入如下代碼:

mul_value = 0
def mul_numbers(num1, num2):
        mul_value = num1 * num2
        print("Local Value:", mul_value)
        return mul_value

mul_numbers(58, 77)
print("Global Value:", mul_value)

運行程序艾蓝,將會看到如下的輸出:

vagrant@python-scripting:~$ python3 -m cProfile cprof_example.py
Local Value: 4466
Global Value: 0
         6 function calls in 0.001 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.001    0.001 cprof_example.py:1(<module>)
        1    0.000    0.000    0.000    0.000 cprof_example.py:2(mul_numbers)
        1    0.000    0.000    0.001    0.001 {built-in method builtins.exec}
        2    0.001    0.000    0.001    0.000 {built-in method builtins.print}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

因此,使用cProfile斗塘,所有調(diào)用的函數(shù)都被打印赢织,并包含各個函數(shù)所消耗的時間。下面我們來看看這些列頭的含義:

  • ncalls: 調(diào)用次數(shù)
  • tottime: 給定函數(shù)花費的總時間
  • percall: tottime除心ncalls所得的商
  • cumtime: 當(dāng)前以及其子函數(shù)所花費的累計時間
  • percall: cumtime除以原始調(diào)用所得的商
  • filename:lineno(function): 提供函數(shù)各自的數(shù)據(jù)

timeit

timeit是一個Python模塊逛拱,用于對Python腳本的各部分進行計時敌厘。我們可以在命令行中調(diào)用timeit,也可以在腳本中導(dǎo)入timeit模塊朽合。下面我們來編寫一個腳本來對代碼片斷進行計時俱两。創(chuàng)建一個timeit_example.py腳本并編寫如下內(nèi)容:

import timeit

prg_setup = "from math import sqrt"
prg_code = '''
def timeit_example():
        list1 = []
        for x in range(50):
                list1.append(sqrt(x))
'''

# timeit 語句
print(timeit.timeit(setup = prg_setup, stmt = prg_code, number = 10000))

運行結(jié)果:

vagrant@python-scripting:~$ python3 timeit_example.py
0.0010215669999524835

使用timeit,,我們可以決定要對哪段代碼進行性能的度量曹步。因此宪彩,我們可以輕易地定義setup代碼來作為我們想單獨執(zhí)行測試的代碼片斷。主代碼默認運行100萬次讲婚,但setup代碼僅運行一次尿孔。

加速程序運行

有很多方式來讓Python程序運行得更快,比如:

  • 對認定為瓶頸的代碼進行性能分析
  • 使用內(nèi)置函數(shù)和庫筹麸,這樣解釋器不用執(zhí)行不同循環(huán)
  • 避免使用全局變量活合,因為Python在訪問全局變量時速度很慢
  • 使用已有的包

總結(jié)

在本章中,我們學(xué)習(xí)了調(diào)試程序和性能分析的重要性物赶。還學(xué)習(xí)用于調(diào)試的不同技術(shù)白指。我們學(xué)習(xí)了pdb Python調(diào)試器以及如何處理異常。還學(xué)習(xí)了如何使用Python中的cProfile和timeit模塊來對腳本進行性能和時耗分析酵紫。最后我們學(xué)習(xí)了如何加速腳本的運行告嘲。

下一章中,我們將學(xué)習(xí)Python中的單元測試奖地。我們會學(xué)習(xí)如何創(chuàng)建和使用單元測試橄唬。

??? 第三章-單元測試-單元測試框架的介紹

課后問題

  1. 要調(diào)用程序,使用哪個模塊参歹?
  2. 查看如何在ipython中使用所有的別名函數(shù)和魔法函數(shù)仰楚。
  3. 什么是全局解釋器鎖(Global interpreted lock (GIL))?
  4. PYTHONSTARTUP, PYTHONCASEOK, PYTHONHOME和PYTHONSTARTUP環(huán)境變量的目的是什么?
  5. 以下代碼的輸出是什么缸血?a) [0], b) [1], c) [1, 0], d) [0, 1]
def foo(k):
           k = [1]
   q = [0]
   foo(q)
   print(q)
  1. 以下哪個是無效變量蜜氨?
    a) my_string_1
    b) 1st_string
    c) foo
    d) _

擴展閱讀

本文首發(fā)地址:Alan Hou 的個人博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捎泻,隨后出現(xiàn)的幾起案子飒炎,更是在濱河造成了極大的恐慌,老刑警劉巖笆豁,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郎汪,死亡現(xiàn)場離奇詭異,居然都是意外死亡闯狱,警方通過查閱死者的電腦和手機煞赢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哄孤,“玉大人照筑,你說我怎么就攤上這事∈莩拢” “怎么了凝危?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晨逝。 經(jīng)常有香客問我蛾默,道長,這世上最難降的妖魔是什么捉貌? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任支鸡,我火速辦了婚禮,結(jié)果婚禮上趁窃,老公的妹妹穿的比我還像新娘牧挣。我一直安慰自己,他們只是感情好醒陆,可當(dāng)我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布浸踩。 她就那樣靜靜地躺著,像睡著了一般统求。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上据块,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天码邻,我揣著相機與錄音,去河邊找鬼另假。 笑死像屋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的边篮。 我是一名探鬼主播己莺,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼奏甫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凌受?” 一聲冷哼從身側(cè)響起阵子,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胜蛉,沒想到半個月后挠进,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡誊册,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年领突,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片案怯。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡君旦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嘲碱,到底是詐尸還是另有隱情金砍,我是刑警寧澤,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布悍汛,位于F島的核電站捞魁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏离咐。R本人自食惡果不足惜谱俭,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宵蛀。 院中可真熱鬧昆著,春花似錦、人聲如沸术陶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梧宫。三九已至接谨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間塘匣,已是汗流浹背脓豪。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留忌卤,地道東北人扫夜。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親笤闯。 傳聞我的和親對象是個殘疾皇子堕阔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,930評論 2 361

推薦閱讀更多精彩內(nèi)容