# Python function 函式呼叫 / 錯誤例外處理 exception
學習如何看懂Python官方手冊,以及語法規則,職訓所課堂筆記。
> [TOC]
---
臨時整理區:
魔法函式
`__name__ == "__main__"` 直接執行而非被載入
https://pythonbook.cc/chapters/basic/module-and-package#被匯入-vs-直接執行
---
<!-- [Python 課堂講義筆記](https://absorbing-bookcase-ffc.notion.site/2024-07-31-79b30c36611940e9bec9ab1f23d957fa) -->
# 呼叫規則
先定義好 function
```
def func(區域參數):
主要程式
return 返回值
```
```python=
# 參數可設預設值,必須固定值
def twoSum(a, b = 5):
"""
兩個數字加起來
"""
c = a + b
return c
```
調用函式
```python=
# 參數傳遞 a,b
a, b = 2, 3
ans = twoSum(a, b)
print(ans)
```
## 局部引數
```python=
def print_test():
mes = "god"
print(mes)
print_test()
# print(mes) 無法從外部呼叫
```
不行設定成可變物件
mutable object
> 課後作業:什麼是 mutable?
必須指向不可變對象
> 預設值盡量不要用空串列或字典
```pyhton=
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
# 輸出
[1]
[1, 2]
[1, 2, 3]
```
需改成
```python=
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
# [1]
# [2]
# [3]
```
## 全域引數
```python=
mes = "god"
def print_test():
global mes
mes = "變更參數"
print(mes)
print_test()
print(f"再次查看:{mes}")
變更參數
再次查看:變更參數
```
# 有內定鍵值的引數
有內定值的引數要放在後面
https://docs.python.org/3/tutorial/controlflow.html#keyword-arguments
* 位置對應引數 postion arguments
* 關鍵字對應引數 keyword(s) arguments
沒講清楚,預設對位置
混合使用,需要先對位置再用 keyword
```python=
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
# parrot("123", state=1000)
```
# 不固定數量引數
查找語法手冊看到:
`breakpoint(*args,**kws)`
可變引數(\*args 和 \*\*kwargs)
(有先後順序,先一顆星,再來兩顆星)
* 一顆星 包住 元組 (a,b)
* 兩顆星 包住 字典 {key:value}
(感覺很高手的做法,竟然可以把 key 傳入變成字典)
字典跑迴圈,拿出來的是鍵值 key
可以再用 `key` 取值,`dic[key]`
* 提醒 key 只能是字串,元素,元組
```python=
def func(a,*t, **d):
for i in range(a):
print("*")
print(t)
print(d)
for key in d:
print(key,d[key])
func(4,"abc","def","ghi",k=1,m=2)
# *
# *
# *
# *
# ('abc', 'def', 'ghi')
# {'k': 1, 'm': 2}
# k 1
# m 2
```
```python=
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])
```
```python=
def add_all(*add):
result = 0
for i in range(len(add)):
result += add[i]
return result
print(add_all(1, 2, 3, 4, 5))
# 15
```
# 特殊參數 Special parameters
https://docs.python.org/3/tutorial/controlflow.html#special-parameters
**範例:**
```python
def func(pos1, pos2, /, pos_or_kwd, *, kwd_only):
print(pos1, pos2, pos_or_kwd, kwd_only)
func(1, 2, 3, kwd_only=4) # 正確
func(1, 2, pos_or_kwd=3, kwd_only=4) # 正確
# func(1, 2, 3, 4) # 錯誤:kwd_only 只能作為關鍵字參數
```
**Argument 跟 Parameters 翻譯差異推論**
- **Parameter(參數)**:函數定義時列出的變數名稱,用來函數呼叫時提供的值。 `def func(a, b=5):`,`a` 和 `b` 是參數。
- **Argument(引數)**:函數被呼叫時實際傳入函數的值。`func(10, 20)` 中,`10` 和 `20` 是傳入的引數。
```
我的練習推論:
e.g : `discord.py` 模組
ctx ---> 作為引數 argument,傳遞上下文
模組底下可以用的方法 run 可以帶入的參數 Parameter
https://discordpy.readthedocs.io/en/stable/api.html?highlight=get%20channel#discord.Client.get_channel
```
三個區域的參數規則
(__第一個__ / 第二個區域 *,__第三個_)
pos only / pos or keyword *, keyword only
例如 BIF函式
> max(iterable, *, key = None)
>print(_*objects_, _sep=' '_, _end='\\n'_, _file=None_, _flush=False_)[¶](https://docs.python.org/3/library/functions.html#print "Link to this definition")
>print(1,2,3) # 123都給object 星號代表全收
>那要怎麼設定後面的參數
>print(1,2,3,sep="#")
>sort(對位置/,*,key=None)
# Unpacking
解包,用更精簡的表達方式取出資料
```
args = [3, 6]
list(range(*args))
```
[`range()`](https://docs.python.org/3/library/stdtypes.html#range "range") function
https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists
https://zhuyinjun.me/2019/python_unpacking_and_packing_1/
https://www.learncodewithmike.com/2019/12/python-unpacking.html
> 課後作業:什麼是 packing unpacking?
> 課後作業:什麼是 mutable inmutable?
留作自學,課堂並未教學
# Documentation Strings
Pycharm 會提示你怎麼使用,就是使用 doc-strings
```python=
def func(x):
"""
:param x: it means....
:return:
"""
match x:
case "最小的":
return 5
case 2:
return 4
case 3:
return 3
case 4:
return 2
case "最大的":
return 1
max(["最小的",2,3,4,"最大的"], key=func)
# 年紀最小的
```
# 例外處理
錯誤類型
分成兩大類: 語法錯誤 與 邏輯錯誤
try...except 是處理邏輯錯誤
> for 迴圈正常結束後 else 才會繼續做
> if 不成立後才執行 else
> try...無錯誤...才執行 else
Python Errors and Exceptions:
https://docs.python.org/3/tutorial/errors.html
Python 所有錯誤資訊:
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
Python 例外處理教學文章:
https://steam.oxxostudio.tw/category/python/basic/try-except.html
https://ithelp.ithome.com.tw/articles/10262492
錯誤訊息:

常見錯誤類型
| 錯誤資訊 | 說明 |
| --- | --- |
| NameError | 使用沒有被定義的對象 |
| IndexError | 索引值超過了序列的大小 |
| TypeError | 數據類型 ( type ) 錯誤 |
| SyntaxError | Python 語法規則錯誤 |
| ValueError | 傳入值錯誤 |
## Try...Except
```
a = "1"
try:
a += 1
print(a)
except TypeError:
print('型別發生錯誤')
except NameError:
print('使用沒有被定義的對象')
print('hello')
```
```
def func(a,b):
try:
c = a/b
# return c
except ZeroDivisionError:
print("Division by zero")
except Exception as e:
print("包山包海",e)
else:
print("else:")
return c
finally:
print("會執行")
```
## Raise
自我拋出錯誤
```
def func(num):
try:
# 要進行錯誤捕捉的程式碼
x = int(input("輸入小於十的正整數: "))
num += x
# x += a # 錯誤測試
if x > 10 or x < 0:
raise ValueError("您輸入的數字必須小於十的正整數") #主動拋出錯誤
except NameError as e:
print("使用沒有被定義的參數 !", e)
except TypeError as e:
print("參數資料類型格式錯誤 !", e)
except ValueError as e:
print("資料傳入錯誤", e)
except KeyboardInterrupt as e:
print("程式被用戶中斷", e)
except EOFError as e:
print("未預期的結束輸入", e)
except Exception as e:
print("總之就是錯了", e)
else:
# 沒有錯誤,就會執行這裡
print(num)
finally:
# 不管有沒有錯誤都會執行這裡
print("一定執行")
```
## Pass
不做任何動作...略過
(非 return)
## Assert
> assert False, "錯誤訊息"
## Finally
程式註解文字引用來源:
https://chwang12341.medium.com/給自己的python小筆記-debug與測試好幫手-嘗試try-except與主動引發raise與assert-程式異常處理-de0099d32bbe
```python=
try:
要進行錯誤捕捉的程式碼
except 錯誤類型a as e: ##e 是用來記住錯誤資訊,可以不寫
如果程式發生錯誤類型為a,就會執行這裡
except 錯誤類型b:
如果錯誤類型為b,就會執行這裡
except (錯誤類型c, 錯誤類型d) as f: ## 用來同時捕捉多個錯誤
如果錯誤類型符合錯誤類型c與錯誤類型d,就會執行這邊
except Exception as e:
如果不知道會發生的錯誤類型為何,可以使用它,除了你寫下要捕捉的錯誤類型,其餘發生的錯誤都會執行這裡
else:
如果沒有錯誤,就會執行這裡
finally:
不管有沒有錯誤都會執行這裡
```
## 冷知識:當參數設定為 "e" 時
https://youtube.com/shorts/Yo6KSHdImkQ?si=W1nnkXYYXPcp2Tuo
## 單元測試框架
unittest
https://docs.python.org/zh-tw/3/library/unittest.html
( 高階使用者使用,日後學習,for 考試先記住規則 )

類別 大寫字母開頭(繼承類別):
def 方法:
def 方法:
> self.assertIsInstance(obj, cls, msg=None)
<!-- 實際範例: -->
# Return
return 返回一個( 任何物件 )
可以是字串,可以是串列,可以是陣列
發現到特別之處,finally 會在 return 前執行。
```
def count(a):
try:
a += 1
except Exception as e:
print("發生異常:", e)
else:
return a
finally:
print("最後執行")
print(count(1))
```
> else 只能接在 except 之後
> finally 會在 return 前執行,finally 若有 return 會覆蓋前面的 return
> 官方手冊案例:只使用 try 跟 finally
> ~~(沒有人會這樣寫吧)~~
> (考題若出答案選錯誤,try 一定要配 except)
https://docs.python.org/3/tutorial/errors.html#defining-clean-up-actions