幾年來我在答疑群,、論壇,、公眾號,、知乎回答的各種問題,沒有一萬也有八千,。其中有三分之二以上都是在幫人看報(bào)錯(cuò),,幫人 debug(調(diào)試代碼)。 可以說,,會不會 debug,,有沒有 debug 的意識,懂不懂 debug 的技巧,,是有沒有入門編程的重要標(biāo)志,。然而絕大多數(shù)的編程書籍和課程都不會強(qiáng)調(diào)這點(diǎn)。教的人和學(xué)的人都只關(guān)注明面上的知識點(diǎn),,卻往往忽略了學(xué)習(xí)的本質(zhì)在于“授之以漁”,。 在我們的碼上行動基礎(chǔ)課程里,《如何 debug》是必須掌握的一個(gè)章節(jié),。后來發(fā)現(xiàn)不少有基礎(chǔ)的同學(xué)直接參與爬蟲課程,,但同樣缺失 debug 的基本功,于是我又把 debug 作為前序章節(jié)加入到爬蟲課程里,。 為什么很多初學(xué)者會覺得編程論壇里的人都不大友好,?我經(jīng)常對提問者說:你這個(gè)問題我無法回答,因?yàn)槟?strong>缺少最基本的調(diào)試信息,。而如果你把調(diào)試信息提供到位了,,基本也就不需要我回答了。歸根結(jié)底,,是從來沒有人告訴過初學(xué)者,,你得先調(diào)試。大部分人都是碰了很多次壁最后終于明白,,啊多么痛的領(lǐng)悟,! 今天我就從 debug 課程里挑選最關(guān)鍵的幾點(diǎn)講一下。不要光 mark,,現(xiàn)在就看一遍,,嫌長就看加粗高亮部分。以后開發(fā)中再遇到問題,,按照以下要點(diǎn)自查,。 1. 選一個(gè)好的 IDE 初學(xué)者一半以上的問題是低級錯(cuò)誤,比如縮進(jìn)錯(cuò)誤(空格,、tab混用),、變量前后命名不一致(拼寫錯(cuò)誤)、函數(shù)調(diào)用時(shí)傳遞的參數(shù)不對,、少引號括號,、用了中文全角等等。這些人眼很難一下發(fā)現(xiàn)的錯(cuò)誤,,代碼編輯器可以在寫代碼的時(shí)候就給你提示出來,。 如圖中,分別有一個(gè)函數(shù)名寫錯(cuò)了,、一個(gè)變量名拼錯(cuò)了,,還有一個(gè) if 前面的空格我用了 Tab 鍵而不是 4 個(gè)空格。Pycharm 在你寫代碼時(shí)就會給出高亮或者波浪線的提示,,把鼠標(biāo)移上去還可以看到錯(cuò)誤的提示,。否則這些錯(cuò)誤是很難被發(fā)現(xiàn)的。 另外,,IDE 的一個(gè)重要功能就是可以自動補(bǔ)全代碼,,省事而且不容易出錯(cuò)。 Python 自帶的 IDLE 在這方面做得并不好,,剛起步時(shí)可以用用,,上手后盡快轉(zhuǎn)。個(gè)人推薦用 Pycharm,,即使是功能相對較少的免費(fèi)社區(qū)版本,,也足夠應(yīng)付開發(fā)時(shí)需求。VSCode 也是很好的選擇,。之前有過兩篇介紹文章,,公眾號里回復(fù) pycharm 可查看。 2. 學(xué)會看報(bào)錯(cuò)信息 絕大多數(shù)問題不是直接在代碼層面就出現(xiàn)的,,而是運(yùn)行時(shí)才發(fā)生,。這時(shí)候正常都會導(dǎo)致程序中斷并輸出報(bào)錯(cuò)信息。一般從報(bào)錯(cuò)信息里就可以看出報(bào)錯(cuò)的位置和原因,。 很多初學(xué)者怕看報(bào)錯(cuò)信息,,這不行。即使英語不好,,也嘗試著去看,,套路就那么些,看幾次你就大概熟悉了,,但對你調(diào)試的幫助非常大,。 不過有時(shí)報(bào)錯(cuò)顯示的位置是有問題的,這種情況通常的原因都是前面某一行的括號或者引號有問題,,而導(dǎo)致后面的代碼錯(cuò)亂,。所以如果報(bào)錯(cuò)指出的位置看不出問題,試著往前看一看,。 在代碼中使用 try...except 可以避免程序因異常中斷,。但在開發(fā)時(shí)不要使用異常處理,,否則阻止了報(bào)錯(cuò)信息的發(fā)出,影響調(diào)試,。 報(bào)錯(cuò)信息還有個(gè)用處就是直接復(fù)制到搜索引擎里進(jìn)行搜索,而這方面 Google 的結(jié)果比百度要好,,英文網(wǎng)站要比中文網(wǎng)站好,。(怎樣用 Google 這個(gè)話題不便在此公開討論。) 3. 別舍不得用 print 這一點(diǎn)是最重要的,! 在程序中輸出并分析 log(日志)是一種很基本但卻很靈活很有效的調(diào)試方式,。使用 print 就是 log 的最常用方法。 log 的作用主要是: a. 確定程序的運(yùn)行路徑,。一個(gè)函數(shù)有沒有被調(diào)用,,一個(gè) if 塊有沒有被執(zhí)行,一個(gè) while 循環(huán)執(zhí)行了幾次,,到了哪一步中斷了,,都可以通過 print 出相關(guān)信息來查看。 b. 查看變量的狀態(tài),。程序自身的報(bào)錯(cuò)會告訴你發(fā)生了什么錯(cuò)誤,,但你還需要找出為什么會發(fā)生錯(cuò)誤。通過 print 輸出出錯(cuò)語句涉及到的相關(guān)變量的值和類型,,可以幫助分析出錯(cuò)原因,。 c. 找出出錯(cuò)位置。往往錯(cuò)誤的原因并不在報(bào)錯(cuò)的位置,,所以多輸出一些標(biāo)記,,多 print 不同位置的變量值,查看變量在運(yùn)行過程中值的變化情況,,可以觀察是在哪里發(fā)生了問題。 幾個(gè)通過 log 調(diào)試的經(jīng)驗(yàn)技巧: a. 多輸出一些輔助信息,,方便自己查看,,不然一堆數(shù)據(jù)看花眼,。比如我一般輸出時(shí)會標(biāo)注上變量名,再輸出變量類型,,以及變量的值。比如: b. 在出錯(cuò)行之前輸出,。報(bào)錯(cuò)行涉及的一些變量,他們的數(shù)值和類型,,全都輸出出來,,看看和預(yù)期是否一致。 c. 一行做一件事,。如果你出錯(cuò)的一行里連續(xù)調(diào)用了多個(gè)函數(shù)或運(yùn)算,請分開寫,,分開輸出,。 d. 對于字符串,直接 print 會被轉(zhuǎn)義和解碼,,影響對變量實(shí)際值的觀察,。可以用 print(repr(text)) 或 print([text]) 的方式查看,。 e. 對于編碼問題,,用好 type 方法和 chardet 庫輔助判斷,。這點(diǎn)之前編碼相關(guān)的文章里有說明,,公眾號里回復(fù) pycharm 可查看。 f. 為了方便記錄和回溯問題日志,,通常也會將 log 輸出到文件,。也有專門的 logging 模塊做這事。 記?。?strong>print 不要錢,,能用多少是多少!確保你清楚程序運(yùn)行的狀態(tài)細(xì)節(jié),。 4. 斷點(diǎn)調(diào)試 斷點(diǎn)調(diào)試看參考之前文章 如何在 Python 中使用斷點(diǎn)調(diào)試,,這里不再復(fù)述。 前面這些基本是操作層面的具體 debug 方法,,下面我再來談點(diǎn) debug 的思想和原則: 1. 縮小出錯(cuò)的范圍 軟件開發(fā)中有一個(gè)“單元測試”的概念:如果你寫了一個(gè)函數(shù),,應(yīng)該先運(yùn)行下這個(gè)函數(shù)是不是正常,各種參數(shù)下會不會出現(xiàn)錯(cuò)誤,。這樣可以把問題控制在較小的范圍內(nèi)解決,。 2. 控制變量法 + 二分查找法 比如做爬蟲沒有抓到正確數(shù)據(jù),那么到底是 (1)請求來的數(shù)值不對,,還是 (2)文本處理方法不對,?如果你認(rèn)為是文本處理方法沒有問題,就手動給定一個(gè)正確格式的字符串文本,應(yīng)該可以得到正確的結(jié)果,。這就相當(dāng)于控制了因素(2),,只改變因素(1),根據(jù)執(zhí)行結(jié)果就可以確認(rèn)或排除問題的所在,。 二分查找法是一個(gè)定位問題的技巧,。如果你的代碼里有問題,那么問題要么在前半段,,要么在后半段,。你先把后半段代碼去掉執(zhí)行,看看程序是否還報(bào)錯(cuò),,就知道錯(cuò)誤在哪部分,。依次類推,不斷折半,,直到找到引發(fā)報(bào)錯(cuò)的代碼,。 3. 先重現(xiàn),再解決,,最后驗(yàn)證 有的錯(cuò)誤不是每次出現(xiàn),。這時(shí)候最好別急著去改代碼,而是想辦法可以穩(wěn)定重現(xiàn)問題,。當(dāng)你能順利重現(xiàn)問題的時(shí)候,,通常離分析解決它也不遠(yuǎn)了。解決了之后,,再按照之前重現(xiàn)的方式驗(yàn)證修改是否確實(shí)有效,。 4. 想清楚再動手 不論是開發(fā)代碼,還是調(diào)試 debug,,在做一個(gè)動作前要清楚自己的目的是什么,,而不是盲目地進(jìn)行改動。不要猜,!我經(jīng)常會看見一些新手遇到問題之后,,反復(fù)執(zhí)行代碼,或者不斷調(diào)整參數(shù),,妄想某次執(zhí)行程序就能神奇地通過了,。這是一個(gè)很不好的態(tài)度,請避免,。要恪守邏輯,,知道現(xiàn)在要解決的問題是什么,需要得到那些信息,,可能的假設(shè)是什么,,如何通過修改代碼去驗(yàn)證你的判斷,,這樣才是合理的 debug 方式。 以上就是一些 debug 的基本思想和技巧,。這里僅僅是概要精華,,操作起來遠(yuǎn)不止這么多,同時(shí)還需要實(shí)際的練習(xí)才能掌握,。我能做的就是給你的編程技能樹下埋一顆種子,,能不能生根發(fā)芽,就看各位自己的澆灌了,。(我相信,,即使我這里都說過了,還是會有很多人依舊犯上述提及的錯(cuò)誤 ╮(╯_╰)╭ 隨緣吧,,師傅領(lǐng)進(jìn)門,,修行在個(gè)人……) |
|