調(diào)試和分析Python腳本

image
  • 來源 | 愿碼(ChainDesk.CN)內(nèi)容編輯
  • 愿碼Slogan | 連接每個程序員的故事
  • 網(wǎng)站 | http://chaindesk.cn
  • 愿碼愿景 | 打造全學(xué)科IT系統(tǒng)免費課程,助力小白用戶、初級工程師0成本免費系統(tǒng)學(xué)習(xí)、低成本進階,幫助BAT一線資深工程師成長并利用自身優(yōu)勢創(chuàng)造睡后收入。
  • 官方公眾號 | 愿碼 | 愿碼服務(wù)號 | 區(qū)塊鏈部落
  • 免費加入愿碼全思維工程師社群 | 任一公眾號回復(fù)“愿碼”兩個字獲取入群二維碼

本文閱讀時長:11min

調(diào)試和分析在Python開發(fā)中發(fā)揮重要作用 。調(diào)試器可幫助程序員分析完整的代碼。調(diào)試器設(shè)置斷點,而分析器運行我們的代碼并向我們提供執(zhí)行時間的詳細信息,分析器將識別程序中的瓶頸。

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


調(diào)試是一個解決代碼中出現(xiàn)的問題并阻止軟件正常運行的過程。在Python中,調(diào)試非常簡單。Python調(diào)試器設(shè)置條件斷點并一次調(diào)試一行源代碼。我們將使用pdb Python標準庫中的模塊調(diào)試我們的Python腳本 。

為了更好地調(diào)試Python程序,可以使用各種技術(shù)。我們將討論Python調(diào)試的四種技術(shù):

  • print() 聲明:這是了解發(fā)生了什么的最簡單方法,因此您可以檢查已執(zhí)行的內(nèi)容。

  • logging:這就像一個print聲明,但有更多的上下文信息,所以你可以完全理解它。

  • pdb debugger:這是一種常用的調(diào)試技術(shù)。使用的優(yōu)點pdb是您可以pdb從命令行,解釋器和程序中使用。

  • IDE調(diào)試器:IDE具有集成調(diào)試器。它允許開發(fā)人員執(zhí)行他們的代碼,然后開發(fā)人員可以在程序執(zhí)行時進行檢查。

錯誤處理(異常處理)


在本節(jié)中,我們將學(xué)習(xí)Python如何處理異常。例外是程序執(zhí)行期間發(fā)生的錯誤。每當(dāng)發(fā)生任何錯誤時,Python都會生成一個異常,該異常將使用try ... except塊進行處理。程序無法處理某些異常,因此會導(dǎo)致錯誤消息。現(xiàn)在,我們將看到一些異常示例。

在終端中,啟動 python3交互式控制臺,我們將看到一些異常示例:

student@ubuntu:~$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> 50 / 0

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

這些是例外的一些例子。現(xiàn)在,我們將看到我們?nèi)绾翁幚懋惓!?/p>

每當(dāng)Python程序中發(fā)生錯誤時,都會引發(fā)異常。我們還可以使用raise關(guān)鍵字強制引發(fā)異常。

現(xià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 = 57
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")

按如下所示運行腳本,您將獲得以下輸出:

student@ubuntu:~$ python3 exception_example.py
The value of c is:  92
Division by zero is not possible
Out of try...except block

調(diào)試器工具


Python支持許多調(diào)試工具:

  • winpdb

  • pydev

  • pydb

  • pdb

  • gdb

  • pyDebug

在本節(jié)中,我們將學(xué)習(xí)pdb Python調(diào)試器。pdbmodule是Python標準庫的一部分,始終可供使用。

pdb調(diào)試器

該pdb模塊用于調(diào)試Python程序。Python程序使用pdb交互式源代碼調(diào)試器來調(diào)試程序。pdb設(shè)置斷點并檢查堆棧幀,并列出源代碼。

現(xiàn)在我們將了解如何使用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)試器。

在解釋器中


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

啟動python3交互式控制臺。運行以下命令以啟動控制臺:

$ python3

導(dǎo)入我們的 pdb_example腳本名稱和pdb模塊。現(xiàn)在,我們將使用run()并且我們將字符串表達式作為參數(shù)傳遞給run()Python解釋器本身:

student@ubuntu:~$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import pdb_example
>>> import pdb
>>> pdb.run('pdb_example.Student(5).print_std()')
> (1)()
(Pdb)

要繼續(xù)調(diào)試,請continue在(Pdb)提示符后輸入并按Enter鍵。如果你想知道我們可以在這里使用的選項,那么在(Pdb)提示后按兩次Tab 鍵。

現(xiàn)在,輸入后continue,我們將獲得如下輸出:

student@ubuntu:~$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import pdb_example
>>> import pdb
>>> pdb.run('pdb_example.Student(5).print_std()')
> (1)()
(Pdb) continue
0
1
2
3
4
>>> 

從命令行


運行調(diào)試器的最簡單,最直接的方法是從命令行。我們的程序?qū)⒆鳛檎{(diào)試器的輸入。您可以從命令行使用調(diào)試器,如下所示:

$ python3 -m pdb pdb_example.py

從命令行運行調(diào)試器時,將加載源代碼,它將停止在找到的第一行執(zhí)行。輸入continue以繼續(xù)調(diào)試。這是輸出:

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

在Python腳本中


前兩種技術(shù)將在Python程序開始時啟動調(diào)試器。但這第三種技術(shù)最適合長期運行的流程。要在腳本中啟動調(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)在,按如下方式運行程序:

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

set_trace() 是一個Python函數(shù),因此您可以在程序中的任何位置調(diào)用它。

因此,這些是啟動調(diào)試器的三種方式。

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


在本節(jié)中,我們將看到跟蹤模塊。跟蹤模塊有助于跟蹤程序執(zhí)行。因此,每當(dāng)您的Python程序崩潰時,我們都可以理解崩潰的位置。我們可以通過將跟蹤模塊導(dǎo)入您的腳本以及命令行來使用它。

現(xiàn)在,我們將創(chuàng)建一個名為腳本trace_example.py并在腳本中編寫以下內(nèi)容:

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()

輸出如下:

student@ubuntu:~$ python3 -m trace --trace trace_example.py
 --- modulename: trace_example, funcname: 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 --trace在命令行使用,開發(fā)人員可以逐行跟蹤程序。因此,只要程序崩潰,開發(fā)人員就會知道崩潰的實例。

分析和計時程序


分析Python程序意味著測量程序的執(zhí)行時間。它衡量每個功能所花費的時間。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)

運行程序,您將看到如下輸出:

student@ubuntu:~$ python3 -m cProfile cprof_example.py
Local Value:  4466
Global Value:  0
         6 function calls in 0.000 seconds
   Ordered by: standard name
 
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 cprof_example.py:1()
        1    0.000    0.000    0.000    0.000 cprof_example.py:2(mul_numbers)
        1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}
        2    0.000    0.000    0.000    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ù)所花費的時間。現(xiàn)在,我們將看到這些列標題的含義:

· ncalls: 通話次數(shù)

· tottime: 在給定函數(shù)中花費的總時間

· percall:商數(shù)tottime除以ncalls

· cumtime:在此和所有方面花費的累計時間 subfunctions

· percall:cumtime除以原始調(diào)用的商數(shù)

· filename:lineno(function):提供每個功能的相應(yīng)數(shù)據(jù)

timeit

timeit是一個Python模塊,用于計算Python腳本的一小部分。您可以從命令行調(diào)用timeit,也可以將timeit模塊導(dǎo)入到腳本中。我們將編寫一個腳本來計算一段代碼。創(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 statement
print(timeit.timeit(setup = prg_setup, stmt = prg_code, number = 10000))

使用timeit,我們可以決定我們要測量的代碼片段。因此,我們可以輕松定義設(shè)置代碼以及我們要單獨執(zhí)行測試的代碼段。主代碼運行100萬次,這是默認時間,而設(shè)置代碼只運行一次。

使程序運行得更快


有多種方法可以使Python程序運行得更快,例如:

  • 描述您的代碼,以便識別瓶頸

  • 使用內(nèi)置函數(shù)和庫,因此解釋器不需要執(zhí)行循環(huán)

  • 避免使用全局變量,因為Python在訪問全局變量時非常慢

  • 使用現(xiàn)有包

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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