## ## 異常處理 - python 可以讓我們去捕捉異常跟撰寫異常處理程序 ```python # 除數為0 def division(x, y): return x / y print(division(10, 2)) print(division(6, 3)) ``` ![upgit_20240816_1723809889.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240816_1723809889.png) - 我們可以使用`try except來捕捉異常` ```python def division(x, y): try: # try - except指令 return x / y except ZeroDivisionError: # 除數為0時執行 print("除數不可為0") print(division(10, 2)) # 5.0 print(division(5, 0)) # 除數不可為0 print(division(6, 3)) # 2.0 ``` - 可以再增加else - try -> 預想可能會發生錯誤的指令 - except -> 抓錯 - else -> 沒事,繼續跑 ```python def division(x, y): try: # try - except指令 ans = x / y except ZeroDivisionError: # 除數為0時執行 print("除數不可為0") else: return ans # 傳回正確的執行結果 print(division(10, 2)) print(division(5, 0)) print(division(6, 3)) ``` ## 找不到檔案 file not found error ```python fn = 'data.txt' try: with open(fn) as file_Obj: data = file_Obj.read() except FileNotFoundError: print(f"找不到 {fn} 檔案") else: print(data) ``` ## 設計多組異常處理程序 ### 常見異常處裡 ![upgit_20240816_1723810259.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240816_1723810259.png) ```python # 通用錯誤 def division(x, y): try: return x / y except Exception as e: print("有錯誤") print(division(10, 2)) print(division(5, 0)) # print(division('a', 'b')) print(division(6, 3)) ``` ```python def division(x, y): try: return x / y except (ZeroDivisionError, TypeError) as e: # 2個異常 print(e) print(division(10, 2)) print(division(5, 0)) # division by zero print(division('a', 'b')) # unsupported operand type(s) for /: 'str' and 'str' print(division(6, 3)) ``` ```python # 通用錯誤 def division(x, y): try: return x / y except : print("有錯誤") print(division(10, 2)) print(division(5, 0)) # print(division('a', 'b')) print(division(6, 3)) ``` ## 丟出異常raise - 前面的部分,著重於python發現異常後,丟出異常,如果不處理就中止執行 - 如果要改為,發現異常後會丟出異常,並且跳到設計好的except去處理 ```python def passWord(pwd): """檢查密碼長度必須是5到8個字元""" pwdlen = len(pwd) # 密碼長度 if pwdlen < 5: # 密碼長度不足 raise Exception('password too short') if pwdlen > 8: # 密碼長度太長 raise Exception('password too long') print('correct') for pwd in ('aaabbbccc', 'aaa', 'aaabbb'): # 測試系列密碼值 try: passWord(pwd) except Exception as e: print(str(e)) # password too long # password too short # correct ``` ## 紀錄 Traceback 字串 - 將報錯放到記事本中 ```python import traceback def passWord(pwd): """檢查密碼長度必須是5到8個字元""" pwdlen = len(pwd) # 密碼長度 if pwdlen < 5: # 密碼長度不足 raise Exception('password too short') if pwdlen > 8: # 密碼長度太長 raise Exception('password too long') print('correct') for pwd in ('aaabbbccc', 'aaa', 'aaabbb'): # 測試系列密碼值 try: passWord(pwd) except Exception as e: print(str(e)) errlog = open('err.txt', 'a') # 開啟錯誤檔案 errlog.write(traceback.format_exc()) # 寫入錯誤檔案 errlog.close() # 關閉錯誤檔案 # password too long # password too short # correct ``` ## finally - 他是【是否有錯】,都一定會執行 ```python def division(x, y): try: print(x / y) except Exception as e: print(e) finally: # 離開函數前先執行此程式碼 print("complete") division(10, 2) division(5, 0) division('a', 'b') division(6, 3) # 5.0 # complete # division by zero # complete # unsupported operand type(s) for /: 'str' and 'str' # complete # 2.0 # complete ``` ## 程式斷言 assert - 主要用於開發階段做檢查 ```python class Banks(): # 定義銀行類別 title = 'Tainan Bank' def __init__(self, uname, money): self.name = uname self.balance = money def save_money(self, money): # 設計存款方法 self.balance += money print("save money", money) def withdraw_money(self, money): # 設計提款方法 self.balance -= money # 執行提款 print("Withdrawal money", money) def get_balance(self): # 獲得存款餘額 print("Current balance ", self.balance) hungbank = Banks('TA', 100) hungbank.get_balance() # 獲得存款餘額 hungbank.save_money(-300) # 存款-300元 hungbank.get_balance() # 獲得存款餘額 hungbank.withdraw_money(700) # 提款700元 hungbank.get_balance() # 獲得存款餘額 # Current balance 100 # save money -300 # Current balance -200 # Withdrawal money 700 # Current balance -900 ``` - 之所以會錯誤是因為,我們在提款前,應該要先檢查帳戶餘額 ```python class Banks(): # 定義銀行類別 title = 'Tainan Bank' def __init__(self, uname, money): self.name = uname self.balance = money def save_money(self, money): # 設計存款方法 self.balance += money print("save money", money) def withdraw_money(self, money): # 設計提款方法 assert money > 0, 'withdraw must > 0' assert money <= self.balance, 'money not enough' self.balance -= money # 執行提款 print("Withdrawal money", money) def get_balance(self): # 獲得存款餘額 print("Current balance ", self.balance) hungbank = Banks('TA', 100) hungbank.get_balance() # 獲得存款餘額 hungbank.save_money(-300) # 存款-300元 hungbank.get_balance() # 獲得存款餘額 hungbank.withdraw_money(700) # 提款700元 hungbank.get_balance() # 獲得存款餘額 # Current balance 100 # save money -300 # Current balance -200 # Withdrawal money 700 # Current balance -900 ``` ![upgit_20240816_1723812366.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240816_1723812366.png) ## 程式日誌模組 logging ### logging level(低到高) - DEBUG : - 用於顯示程式的小細節,是最低層級的內容。 - 通常在調試程式問題時使用,可追蹤關鍵變數的變化過程。 - INFO : - 用於記錄程式一般發生的事件。 - WARNING : - 用於顯示可能影響程式執行但尚未造成問題的事件,未來可能導致問題的發生。 - ERROR : - 顯示程式發生的錯誤,通常是在某些狀態下引發錯誤的原因。 - CRITICAL : - 通常表示將導致系統崩潰或中斷的錯誤。 ```python logging.basicConfig(level=logging.DEBUG, format="") # 等級是DEBUG logging.debug('logging message, DEBUG') logging.info('logging message, INFO') logging.warning('logging message, WARNING') logging.error('logging message, ERROR') logging.critical('logging message, CRITICAL') # logging message, DEBUG # logging message, INFO # logging message, WARNING # logging message, ERROR # logging message, CRITICAL ``` ```python logging.basicConfig(level=logging.WARNING, format="") # 等級是DEBUG logging.debug('logging message, DEBUG') logging.info('logging message, INFO') logging.warning('logging message, WARNING') logging.error('logging message, ERROR') logging.critical('logging message, CRITICAL') # logging message, WARNING # logging message, ERROR # logging message, CRITICAL ``` ```python # 列出時間、等級、地點 logging.basicConfig(filename="logging.txt" level=logging.WARNING, format='%(asctime)s - %(levelname)s : %(message)s') # 等級是DEBUG logging.debug('logging message, DEBUG') logging.info('logging message, INFO') logging.warning('logging message, WARNING') logging.error('logging message, ERROR') logging.critical('logging message, CRITICAL') # logging message, WARNING # logging message, ERROR # logging message, CRITICAL ``` - 最後要停用logging ```python logging.disable(logging.CRITICAL) ``` # module & package - 以下是python常見的module ![upgit_20240817_1723873329.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240817_1723873329.png) - 外部module會使用pip去安裝 ## 自己建立模組 ```python def make_icecream(*toppings): """這邊寫關於函數的說明 Args: toppings (string): 各種配料 Return: none """ print("這個冰淇淋所加配料如下") for topping in toppings: print("--- ", topping) def make_drink(size, drink): """這邊寫關於函數的說明 Args: size (string): drink (string): 各種飲料 Return: none """ print("所點飲料如下") print("--- ", size.title()) print("--- ", drink.title()) make_icecream('草莓醬') make_icecream('草莓醬', '葡萄乾', '巧克力碎片') make_drink('large', 'coke') # 這個冰淇淋所加配料如下 # --- 草莓醬 # 這個冰淇淋所加配料如下 # --- 草莓醬 # --- 葡萄乾 # --- 巧克力碎片 # 所點飲料如下 # --- Large # --- Coke ``` - 那我覺得我知還會再用到這個func,所以我把它放到module ## 應用自己寫的module - 副檔名為py ```python %%writefile make_food.py def make_icecream(*toppings): """這邊寫關於函數的說明 Args: toppings (string): 各種配料 Return: none """ print("這個冰淇淋所加配料如下") for topping in toppings: print("--- ", topping) def make_drink(size, drink): """這邊寫關於函數的說明 Args: size (string): drink (string): 各種飲料 Return: none """ print("所點飲料如下") print("--- ", size.title()) print("--- ", drink.title()) ``` - 要使用的地方把它引入 `from module_name import func_name` ```python from make_food import make_icecream make_icecream('草莓醬', '葡萄乾', '巧克力碎片') # 這個冰淇淋所加配料如下 # --- 草莓醬 # --- 葡萄乾 # --- 巧克力碎片 ``` - 如果要導入多組函數 `from make_food import make_icecream, make_drink` - 如果要導入所有函數 `from make_food import *` ### module 使用別稱as - 可以分為: - 幫module取名 - 幫module的func取名 ```python from make_food import make_icecream as m m('草莓醬', '葡萄乾', '巧克力碎片') ``` ```python import make_food as m m.make_icecream('草莓醬', '葡萄乾', '巧克力碎片') ``` ## if name == "main" ## 將自己寫的class建立在module內 - 類別也可以用module的方式去處理 ```python class Banks(): def __init__(self, name): self.__name = name self.__balance = 0 self.__title = "Bank" def save_money(self, money): # 設計存款方法 self.__balance += money print("存款 ", money, " 完成") def withdraw_money(self, money): # 設計提款方法 self.__balance -= money print("提款 ", money, " 完成") def get_balance(self): # 獲得存款餘額 print(self.__name.title(), " 目前餘額: ", self.__balance) def bank_title(self): # 獲得銀行名稱 return self.__title class Tainan_bank(Banks): def __init__(self, name): self.__title = "Tainan bank" def bank_title(self): # 獲得銀行名稱 return self.__title ``` ## 應用自己寫的class ```python from make_band import Banks, Tainan_bank TA = Banks('James') print("TA's banks = ", TA.bank_title()) # 列印銀行名稱 TA.save_money(500) # 存錢 TA.get_balance() # 列出存款金額 hung = Tainan_bank('Hung') # 定義Shilin_Banks類別物件 print("hung's banks = ", hung.bank_title()) # 列印銀行名稱 ``` ## random module ![upgit_20240817_1723894981.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240817_1723894981.png) ```python import random random.seed(24) print(random.randint(1, 10)) # 隨機整數 print(random.random()) # 0-1 隨機浮點數 print(random.uniform(1,3)) # 範圍間隨機浮點數 print(random.choice(["a", "b", "c"])) # 範圍間隨機選 print(random.sample(["a", "b", "c"], 2)) # 範圍間隨機選要求數量 # 隨機排列 arr = ["a", "b", "c"] random.shuffle(arr) print(arr) # 7 # 0.8397997363118733 # 1.365183773909035 # a # ['a', 'c'] # ['b', 'a', 'c'] ``` ## time module ![upgit_20240817_1723895801.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240817_1723895801.png) ```python import time print(time.time()) # 1970.01.01到現在秒數 print(time.ctime()) # 目前系統時間 print(time.localtime()) # 返回tuple結構時間 print(time.localtime().tm_year) print(time.localtime()[0]) # 1723896127.5555916 # Sat Aug 17 20:02:07 2024 # time.struct_time(tm_year=2024, tm_mon=8, tm_mday=17, tm_hour=20, tm_min=2, tm_sec=7, tm_wday=5, tm_yday=230, tm_isdst=0) # 2024 # 2024 ``` ## keyword module - 檢查關鍵字 ```python import keyword print(keyword.kwlist) # 列出所有關鍵字 # 檢查關鍵字 arr = ['False', 'and', 'as'] for i in arr: print(keyword.iskeyword(i)) # ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield'] # True # True # True ``` ## sys module - 可以了解python shell的訊息 ```python import sys # 顯示python訊息 print(sys.version) print(sys.version_info) # 比較結構化 ``` ```python ##### stdin ##### # standard input 的縮寫 import sys # 要額外開+terminal print("請輸入字串") msg = sys.stdin.readline() print(msg) # 規定要取幾個字元 print("請輸入字串") msg = sys.stdin.readline(3) print(msg) ``` ```python ##### stdout ##### # standard output 的縮寫 import sys # 要額外開+terminal sys.stdout.write("122323") ``` ```python # 列出module所在的路徑 import sys for i in sys.path: print(i) # D:\anaconda3\python312.zip # D:\anaconda3\DLLs # D:\anaconda3\Lib # D:\anaconda3 # D:\anaconda3\Lib\site-packages # D:\anaconda3\Lib\site-packages\win32 # D:\anaconda3\Lib\site-packages\win32\lib # D:\anaconda3\Lib\site-packages\Pythonwin ``` ## module searching - 針對模組放的路徑,可以有一下選擇: - 放在該專案資料夾裡面的 sys.path - 跟 main program 放在一起 - 放在任何地方,再去更改 sys.path ```python import sys # 電腦會根據以下每一個路徑去找module print(*sys.path, sep="\n") # c:\github\python-study-notes\python基本教學 # c:\Users\33313\anaconda3\python311.zip # c:\Users\33313\anaconda3\DLLs # c:\Users\33313\anaconda3\Lib # c:\Users\33313\anaconda3 # ... ``` ```python # TODO:更改sys.path sys.path.append("要增加的路徑") ``` ## namespace - 其實就是前面提到 LEGB 規則的延伸,程式在找變數(函數),會先從區域命名空間(local namespace)去找,接著是全域命名空間(global namespace),最後是 built-in(內建函式庫) ```python # 輸出內建函式庫 print(__builtins__) # <module 'builtins' (built-in)> # 輸出所有函數名稱 print(*dir(__builtins__), sep="\n") # ArithmeticError # AssertionError # AttributeError # BaseException # ... ``` ## package - package 是一種包含很多 python 模組的字典 - PyPI:全名 Python Package Index,是指 Python 生態系統中的一個中央軟件存儲庫。 - Pip:安裝第三方庫的指令: ```terminal= # TODO: pip相關指令 pip install <package_name> pip install -upgrade <package_name> # 安裝指定版本 pip install <package_name> == <指定版本> # 確認目前安裝所有模組及版本 pip freeze ``` ## Lab07作業題目 - 繳交方式 :請到https://140.116.179.59:8080完成作業題目,並將程式碼加上註解(你的理解),很重要,否則助教有權利扣你分數 - 禁止抄襲,否則助教會來查水表。 - 本次Lab