---
# System prepended metadata

title: module & package
tags: [計算機概論]

---

## ## 異常處理
- 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

