今天是看這本書Automate the Boring Stuff with Python(Python編程快速上手--讓繁瑣工作自動化)
第10章節(Organizing Files)所做的練習
這章主要學習透過日誌, 斷言和調試器, 發現程式碼的問題並修復.
raise 搭配Exception函數調用, 可用來包含有用的出錯訊息
>>> raise Exception('This is the error message')
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
raise Exception('This is the error message')
Exception: This is the error message
>>>
先讓我們用個印出方框的程式來學習
>>> def boxprint(symbol, width, height):
# 印出頂部
print(symbol * width)
# 印出中間
for i in range(height - 2):
print(symbol + (' ' * (width - 2)) + symbol)
# 印出底部
print(symbol * width)
>>> boxprint('*', 15, 5)
***************
* *
* *
* *
***************
>>>
這程式看似完美, 但如果給予的參數有兩個** 或 寬<=2 或 高<=2
程式都能運行, 且不會顯示錯誤. 但結果卻不會是我們想要的方框.
>>> boxprint('**', 1, 2)
**
**
>>>
使用raise 搭配Exception函數調用, 就能拋出異常, 並讓我了解錯誤之處
>>> def boxprint(symbol, width, height):
# 若參數符號不為1, 則拋出異常.
if len(symbol) != 1:
raise Exception('Symbol must be a single character string')
# 若參數寬<=2, 則拋出異常.
if width <= 2:
raise Exception('Width must be grester than 2')
# 若參數高<=2, 則拋出異常.
if height <= 2:
raise Exception('height must be grester than 2')
# 印出頂部
print(symbol * width)
# 印出中間
for i in range(height - 2):
print(symbol + (' ' * (width - 2)) + symbol)
# 印出底部
print(symbol * width)
>>> boxprint('**', 5, 5)
Traceback (most recent call last):
File "<pyshell#73>", line 1, in <module>
boxprint('**', 5, 5)
File "<pyshell#72>", line 3, in boxprint
raise Exception('Symbol must be a single character string')
Exception: Symbol must be a single character string
>>>
Python若遇到程式碼錯誤, 會生成一些錯誤訊息. 稱為反向跟蹤(Trackback)
可以透過調用trackback.format_exec()來得到反向跟蹤的字符串型式.
首先先寫一個會發生錯過的程式碼, 並用raise 搭配Exception函數來產生我們要的錯誤訊息.
其執行結果可以很清楚的看到, 在bacon()的第二行產生了我們要的錯誤訊息
>>> def spam():
bacon()
>>> def bacon():
raise Exception('This is the error message')
>>> spam()
Traceback (most recent call last):
File "<pyshell#103>", line 1, in <module>
spam()
File "<pyshell#99>", line 2, in spam
bacon()
File "<pyshell#102>", line 2, in bacon
raise Exception('This is the error message')
Exception: This is the error message
>>>
調用trackback.format_exec()來得到反向跟蹤的字符串型式.
需要先import trackback. 然後再開啟一個errorInfo.txt檔 並把traceback的訊息寫入.
import traceback
try:
raise Exception('This is the error message')
except:
errorFile = open('errorInfo.txt', 'w')
errorFile.write(traceback.format_exc())
errorFile.close()
print('The traceback info was written to errorInfo.txt')
def spam():
bacon()
def bacon():
raise Exception('This is the error message')
spam()
errorInfo.txt的內容如下:
Traceback (most recent call last):
File "D:/Tech/Blog/Python/Automate The Boring Stuff With Python/Chapter 10/10_2.py", line 3, in <module>
raise Exception('This is the error message')
Exception: This is the error message
斷言(Assert), 就是設定一個條件為真, 當這條件不為真時.
雖然程式沒有崩潰, 但還是有缺陷. 提示出字符串
首先寫一個交通燈程式:
街道1和街道2的十字路口, 有南北向與東西向的交通燈.
交通燈的順序為綠>黃>紅.
street1_street2 = {'ns': 'green', 'ew': 'red'}
def switchlights(stoplight):
for key in stoplight.keys():
if stoplight[key] == 'green':
stoplight[key] = 'yellow'
elif stoplight[key] == 'yellow':
stoplight[key] = 'red'
elif stoplight[key] == 'red':
stoplight[key] = 'green'
print('First Time: ' + str(street1_street2))
switchlights(street1_street2)
print('Second Time: ' + str(street1_street2))
---------------其運行結果為:---------------
First Time: {'ns': 'green', 'ew': 'red'}
Second Time: {'ns': 'yellow', 'ew': 'green'}
可以發現第一次,很正常. 南北向為綠燈, 東西向為紅燈.
第二次就有問題. 南北向不應該為黃燈, 東西向為綠燈.
雖然程式沒有崩潰, 但這結果不是我想要的(當東西向為綠燈時, 南北向應為紅燈, 不該為黃燈)
所以我們加入斷言來檢查, 如果變數stoplight中沒有red之值, 便指出程式碼錯誤.
並產生字浮串(Neither light is red! ' + str(stoplight))
street1_street2 = {'ns': 'green', 'ew': 'red'}
def switchlights(stoplight):
for key in stoplight.keys():
if stoplight[key] == 'green':
stoplight[key] = 'yellow'
elif stoplight[key] == 'yellow':
stoplight[key] = 'red'
elif stoplight[key] == 'red':
stoplight[key] = 'green'
assert 'red' in stoplight.values(), 'Neither light is red! ' + str(stoplight)
print('First Time: ' + str(street1_street2))
switchlights(street1_street2)
print('Second Time: ' + str(street1_street2))
---------------其運行結果為:---------------
First Time: {'ns': 'green', 'ew': 'red'}
Traceback (most recent call last):
File "D:/Tech/Blog/Python/Automate The Boring Stuff With Python/Chapter 10/10_3.py", line 17, in <module>
switchlights(street1_street2)
File "D:/Tech/Blog/Python/Automate The Boring Stuff With Python/Chapter 10/10_3.py", line 12, in switchlights
assert 'red' in stoplight.values(), 'Neither light is red! ' + str(stoplight)
AssertionError: Neither light is red! {'ns': 'yellow', 'ew': 'green'}
Python的Logging模組可以很容易創造一個自定義的消息紀錄.
要啟用Logging模組要先輸入下列程式碼
import logging
logging .basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
然後寫一個計算價乘的程式, 比如1*2*3*4*5等於多少
並放入logging訊息
import logging
logging .basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
logging.debug('Start of program')
def factorial(n):
logging.debug('Start of factorial(%s)' % (n))
total = 1
for i in range(n + 1):
total *= i
logging.debug('i is ' + str(i) + ', total is ' + str(total))
logging.debug('End of factorial(%s)' % (n))
return total
print(factorial(5))
logging.debug('End of program')
---------------其運行結果為:---------------
0
2020-06-16 15:32:43,875 - DEBUG - Start of program
2020-06-16 15:32:43,875 - DEBUG - Start of factorial(5)
2020-06-16 15:32:43,875 - DEBUG - i is 0, total is 0
2020-06-16 15:32:43,875 - DEBUG - i is 1, total is 0
2020-06-16 15:32:43,875 - DEBUG - i is 2, total is 0
2020-06-16 15:32:43,875 - DEBUG - i is 3, total is 0
2020-06-16 15:32:43,875 - DEBUG - i is 4, total is 0
2020-06-16 15:32:43,875 - DEBUG - i is 5, total is 0
2020-06-16 15:32:43,875 - DEBUG - End of factorial(5)
2020-06-16 15:32:43,875 - DEBUG - End of program
可以發現1*2*3*4*5=0 這是不對的
透過Logging 可以發現 一開始i為0 就不對, 應該把i改為1
for i in range(1, n + 1):
---------------其運行結果為:---------------
2020-06-16 15:38:12,141 - DEBUG - Start of program
2020-06-16 15:38:12,141 - DEBUG - Start of factorial(5)
2020-06-16 15:38:12,141 - DEBUG - i is 1, total is 1
2020-06-16 15:38:12,141 - DEBUG - i is 2, total is 2
2020-06-16 15:38:12,141 - DEBUG - i is 3, total is 6
2020-06-16 15:38:12,141 - DEBUG - i is 4, total is 24
2020-06-16 15:38:12,142 - DEBUG - i is 5, total is 120
2020-06-16 15:38:12,142 - DEBUG - End of factorial(5)
2020-06-16 15:38:12,142 - DEBUG - End of program
120
Logging有五個級別, 由低至高分別為
debug, info, warning, error, critical
可以透過設定logging level來過濾所需要的函數
比如
level = logging.ERROR
這將只顯示error及更高級別的critical, 而跳過其他三個較低級別的訊息
還有一個方法可以做到相同的事
logging.disable(logging.ERROR)
這樣可以停止顯示error及其較低等級的訊息
logging.basicConfig(filename='myProgramLog.txt')
可以將日誌寫入一個文檔存起來
在Python IDLE的Debug > Debugger 可以打開Python的調試器
Go: 可以讓程式一次正常執行到最後
Step: 程式一行行的執行
Over: 跟Step類似, 但會跳過函數調用
Out: 當用step執行並進入入了函數, Out可以直接執行完整個函數調用, 直到從函數返回
Quit: 停止Debugging, 並跳出調試器
章節10習題, 用Notepad++打下列的程式碼,
另存為10_8.py. 我附上中文注釋方便好讀.
# -*- coding: UTF-8 -*-
# http://juilin77.blogspot.com/
# v20200616
# Automate The Boring Stuff With Python - Chapter 10
# ========== 10.8 ==========
import random
guess = input('Guess the coin toss! Enter heads or tails:')
if guess != 'heads' and guess != 'tails':
raise Exception('Must be heads or tails')
guess_toss = {0: 'heads', 1: 'tails'}
toss = guess_toss[random.randint(0, 1)] # 0 is tails, 1 is heads
if toss == guess:
print('You got it!')
else:
guess = input('Nope! Guess again!')
if toss == guess:
print('You got it!')
else:
print('Nope. You are really bad at this game.')
Reference:
Automate the Boring Stuff with Python
Python編程快速上手--讓繁瑣工作自動化
ISBN-10: B01N6B9BSA
https://www.amazon.com/Python%E7%BC%96%E7%A8%8B%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B-%E8%AE%A9%E7%B9%81%E7%90%90%E5%B7%A5%E4%BD%9C%E8%87%AA%E5%8A%A8%E5%8C%96-%E7%BE%8E-Al-Sweigart%EF%BC%88%E6%96%AF%E7%BB%B4%E5%8A%A0%E7%89%B9%EF%BC%89/dp/B01I0XN8XY/ref=sr_1_1?ie=UTF8&qid=1543814481&sr=8-1&keywords=9787115422699
官網:
https://automatetheboringstuff.com/
最初發表 / 最後更新: 2020.06.16 / 2020.06.16
hi Peter 好久不見,我是Johnny 方便加個line嗎 ID: chuang123
回覆刪除