# 從0開始學 Python ## 單元 0:程式語言 - **什麼是程式語言?** - 程式語言是一種讓電腦執行特定指令的方式,就像我們用語言進行溝通,程式語言則是人與電腦之間的溝通工具。 - 程式碼由一行行的**指令**組成,電腦會**依照順序逐行執行**。 - **程式運行的方式** - 逐行執行:電腦會從上到下逐行執行指令,若有錯誤則會停止運行並顯示錯誤訊息。 - 控制流程:透過條件判斷(`if-else`)和迴圈(`for`、`while`),可以改變程式執行的順序。 - **程式碼的組織** - **變數與型別** 變數用來儲存資料,並且可以有不同的型態(型別) 如:**整數**、**小數(浮點數)**、**文字(字串)** 等等 - **輸入與輸出** 提供使用者輸入資料,並且輸出結果給使用者看。 - **資料結構** 利用資料結構可以一次性儲存更多的資料,或者更有結構性的儲存方便管理與操作。 - **運算** 進行數字的算數運算,或是**或、且、非**常見的邏輯運算。 - **條件控制** 給定條件來讓程式跳過特定部分,或是在不同條件執行不同部分。 - **迴圈** 幫助執行需要一直重複的程式碼,包含給定次數與未知次數的重複。 - **函數** 將常用到的功能包裝成一個函數,可以讓程式碼更為整潔、有架構,也可以避免重複寫同一段程式碼。 - **導入函數庫** 可以將別人已經寫好的功能進行導入(import),就不用自己寫。 - **範例程式架構** ```python= # ---------- Import 區塊 ---------- import random # 匯入 random 模組,用於產生隨機數 import time # 匯入 time 模組,用於延遲輸出 # ---------- 變數宣告區塊 ---------- WELCOME_MESSAGE = "歡迎使用 Python 課程範例程式" LOOP_COUNT = 3 # 設定迴圈執行次數 # ---------- 函數定義區塊 ---------- def display_message(message): """顯示訊息並延遲 0.5 秒,模擬程式逐步執行的過程""" print(message) time.sleep(0.5) def generate_random_numbers(count, start=1, end=100): """產生指定數量的隨機數,並回傳列表""" numbers = [] for _ in range(count): numbers.append(random.randint(start, end)) return numbers def analyze_number(num): """判斷數字是偶數或奇數,並回傳對應的描述字串""" if num % 2 == 0: return f"{num} 是偶數" else: return f"{num} 是奇數" # ---------- 程式執行區塊 ---------- display_message(WELCOME_MESSAGE) # 顯示歡迎訊息 # 產生隨機數列表 random_numbers = generate_random_numbers(LOOP_COUNT, 1, 50) # 依序分析每個隨機數 for num in random_numbers: result = analyze_number(num) # 呼叫函數分析數字 display_message(result) # 顯示結果 ``` --- ## 單元 1:變數與型別 - **變數**:變數是用來儲存資料的容器,使用等號 `=` 進行賦值。 - 範例:`x = 10` - **變數命名規則**: - 只能由大寫`[A-Z]`、小寫`[a-z]`、數字`[0-9]`、底線`_`組成。 - 數字不能在開頭 `1num = 10 # 不合法` - **多字詞的命名通則** - Camel Case : `firstNameOfStudent` - Snake Case : `first_name_of_student` - **型別**:Python 中的資料型別包括: - 整數(`int`) - 浮點數(`float`) - 字串(`str`) - 布林值(`bool`):`True`, `False` - **使用 `type()` 函數檢查變數型別。** ```python a = 10.1 print(type(a)) # 輸出: <class 'float'> ``` - **型別轉換** - 使用`int()`、`str()`等就可以進行傳換 ```python a = "123" print(type(a)) # 輸出: <class 'str'> b = int(a) print(type(b)) # 輸出: <class 'int'> ``` ### 範例練習 ```python # 練習 1: 宣告變數並賦值 x = 25 # 整數 y = "18" pi = 3.14 # 浮點數 name = "Python" # 字串 # 練習 2: 使用 type() 函數檢查變數型別 print(type(x)) # 輸出: <class 'int'> print(type(y)) # 輸出: <class 'str'> print(type(pi)) # 輸出: <class 'float'> print(type(name)) # 輸出: <class 'str'> # 練習 3: 轉換變數型別 y = int(y) print(x+y) # 輸出: 43 ``` ### 牛刀小試 1. 宣告一個布林值變數 `is_valid`,並賦值為 `True` 2. 創建一個浮點數變數 `temperature`,賦值為 `36.6`,然後使用 `type()` 函數檢查其型別 --- ## 單元 2:輸入與輸出 - **輸入(Input)**:使用 `input()` 函數從使用者獲取輸入。 - 輸入的值預設為字串,若需其他型別需進行轉換。 ```python # 輸入字串 name = input("名字:") # 輸入轉整數 age = int(input("年齡:")) ``` - **輸出(Output)**:使用 `print()` 函數將資料輸出到螢幕。 - 可以使用逗號 `,`進行多項輸出。 ```python name = "Bob" age = "18" print("名字:", name, "年齡:", age) # 輸出: "名字: Bob 年齡: 18" ``` - **f-string** 使用f開頭的字串,可以在裡面加入參數值方便格式化輸出。 ```python name = "Bob" age = "18" print(f"名字: {name} 年齡: {age}") # 輸出: "名字: Bob 年齡: 18" ``` ### 範例練習 ```python # 練習 1: 簡單的輸入與輸出 name = input("請輸入你的名字:") print(f"你好,{name}!") # 練習 2: 計算兩數相加 num1 = int(input("請輸入第一個數字:")) num2 = int(input("請輸入第二個數字:")) sum_result = num1 + num2 print(f"{num1} + {num2} = {sum_result}") ``` ### 牛刀小試 1. 寫一個程式,讓使用者輸入兩個整數,輸出兩數的乘積 提示:若要取得兩數乘積,可以使用`*`運算。例如`a*b` 2. 接收使用者輸入的名字和年齡,然後輸出問候語:`你好,XXX,你的年齡是 YYY 歲!` --- ## 單元 3:資料結構 ### 陣列(list) - 陣列使用方括號 `[]`,儲存有序資料。 - **索引值與取值**: - 陣列中的元素可以透過索引值(從 0 開始)存取。 - 支援負索引,`-1` 表示最後一個元素,`-2` 表示倒數第二個元素。 ```python numbers = [10, 20, 30, 40] print(numbers[0]) # 輸出: 10 print(numbers[-1]) # 輸出: 40 ``` - **基本操作**: - **新增元素**:`append(value)` - **插入元素**:`insert(index, value)` - **刪除特定位置**:`del` - **刪除特定元素**:`remove()` - **取得特定區段**:`list[a:b:c]` (後面會再做介紹) - **常用函數**: - `split()` 用`split(切割目標)`:切割字串(預設是空格)成陣列 ```python arr = input().split("-") # 輸入 "1-2-3-4-5" # arr = ["1", "2", "3", "4", "5"] ``` - `map()` 用`map(函數, 陣列)`可以快速將陣列每一個值帶入函數中並返回,記得要再外包一層`list()`才會是正常的陣列型態。 ```python arr = list(map(int, arr)) print(arr) # 輸出: [1, 2, 3, 4, 5] ``` ### 範例練習 ```python numbers = [1, 2, 3] numbers.append(4) # 新增 4 print(numbers) # 輸出: [1, 2, 3, 4] numbers.insert(1, 4) # 在索引 1 插入 4 print(numbers) # 輸出: [1, 4, 2, 3, 4] del numbers[2] # 刪除索引 2 的元素 print(numbers) # 輸出: [1, 4, 3, 4] numbers[2] = 10 # 修改索引2的值 print(numbers) # 輸出: [1, 4, 10, 4] numbers.remove(4) # 刪除第一個出現的4 print(numbers) # 輸出: [1, 10, 4] ``` ### 雙層(二維)陣列 - 陣列的元素可以是任何東西,當然也可以是一個陣列! ```python # 定義一個二維陣列 arr arr = [[1, 2, 3], # arr中的第一個陣列 [4, 5, 6], # arr中的第二個陣列 [7, 8, 9]] # arr中的第三個陣列 # 取得arr中第二個陣列 # v x = arr[1][0] # ^ # 取得第1個陣列第一個元素 print(x) # 輸出: 4 ``` ### 牛刀小試 1. 創建一個陣列 `colors`,包含三個顏色:`red`, `green`, `blue`,並用索引值輸出第二個顏色,接著新增顏色`yellow`並刪除掉`blue` 2. 創建一個`3*4`的二維陣列,依序填入`1~12`的數字,並設法利用索引值取值印出`7`這個數字 --- ### 字典(dict) - 字典使用大括號 `{}`,以鍵值對形式儲存資料。 - 基本操作: - **新增/修改鍵值對**:`dict[key] = value` - **刪除鍵值對**:`del dict[key]` - **取得所有鍵或值**:`keys()`、`values()` ```python student = {"name": "Alice", "age": 20} student["grade"] = "A" # 新增鍵值對 student["age"] = 24 # 修改年齡 print(student) # 輸出: {'name': 'Alice', 'age': 24, 'grade': 'A'} del student["age"] # 刪除 age print(student) # 輸出: {'name': 'Alice', 'grade': 'A'} print(list(student.keys())) # 輸出: ['name', 'grade'] print(list(student.values())) # 輸出: ['Alice', 'A'] ``` ### 牛刀小試 1. 使用字典儲存三個人的資料,key值為名字,value為生日 2. 接續上題,新增一個人的資料,並刪除字典中第一個人的資料 --- ## 單元 4:算數運算 - **基本運算符號**: - 加法:`+` - 減法:`-` - 乘法:`*` - 除法:`/` - 整數除法:`//` - 取餘數:`%` - 次方:`**` ### 範例練習 ```python # 基本算數運算 x = 10 y = 3 print(x + y) # 輸出: 13 print(x - y) # 輸出: 7 print(x * y) # 輸出: 30 print(x / y) # 輸出: 3.333333333333333 print(x // y) # 輸出: 3 print(x % y) # 輸出: 1 print(x ** y) # 輸出: 1000 ``` - **賦值運算** 當運算符號後面加上一個`=`,可以在運算過後直接將值賦予給前面的變數。 ```python a = 10 b = 2 a += b # 等價於a = a+b print(a) # 輸出: 12 b *= a # 等價於b = b*a print(b) # 輸出: 24 ``` ### 牛刀小試 1. 給使用者輸入兩個整數,計算前者除以後者的商與餘數 2. 設定變數a b c,輸出$y = ax^2+bx+c$ 方程式中$x$的公式解 $$ \begin{aligned} x1 &= \frac{-b + \sqrt{b^2-4ac}}{2a} \\[6pt] x2 &= \frac{-b - \sqrt{b^2-4ac}}{2a} \end{aligned} $$ --- ## 單元 5:邏輯運算 - **邏輯運算符號**: - 且(`and`): 需要同時滿足 - 或(`or`) : 滿足其中一個 - 非(`not`): 取反向 - **比較符號**: - 等於(`==`) - 不等於(`!=`) - 大於(`>`) - 大於等於(`>=`) - 小於(`<`) - 小於等於(`<=`) ### 範例練習 ```python # 基本邏輯運算 x = True y = False print(x and y) # 輸出: False print(x or y) # 輸出: True print(not x) # 輸出: False a = 10 b = 11 c = 10.0 print(a == b) # 輸出: False print(a == c) # 輸出: True print(a <= b) # 輸出: True ``` ### 牛刀小試 1. 假設 `a = True` 和 `b = False`,計算 `(a and b) or (not b)` 的結果,並用程式驗證 2. 寫一段程式,檢查一個數字是否大於 10 並且是偶數 --- ## 單元 6:if-else 條件控制 - **語法結構**: ```python if 條件: # 當條件為 True 時執行的程式碼 elif 其他條件: # 當其他條件為 True 時執行的程式碼 else: # 當條件均為 False 時執行的程式碼 ``` - **範例**: ```python x = 10 if x > 10: print("x 大於 10") elif x == 10: print("x 等於 10") else: print("x 小於 10") ``` ### 範例練習 ```python # 使用 if-elif-else 判斷成績等級 grade = 85 if grade >= 90: print("A") elif grade >= 80: print("B") elif grade >= 70: print("C") elif grade >= 60: print("D") else: print("F") ``` ### 牛刀小試 1. 編寫程式判斷一個輸入的整數是否為正數、零或負數 2. 編寫程式判斷一個數字是否是 3 或 5 的倍數,或兩者皆是,或都不是 --- ## 單元 7:for 與 while 迴圈 - **for 迴圈**:用於遍歷序列中的每個元素。 ```python for 元素 in 序列: # 執行的程式碼 ``` - 範例 ```python for i in [1, 2, 3]: print(i) # 輸出: # 1 # 2 # 3 ``` - **遍歷字典** - 遍歷字典時會直接遍歷所有的key值 ```py age = {"Alice": 10, "Blice": 13, "Clice": 18} for d in age: print(d) # 輸出: # Alice # Blice # Clice ``` - **使用 `range()`**: - `range()` 函數用於生成一系列連續的數字,適合用於 for 迴圈。 - 語法: ```python range(start, stop, step) ``` - 預設值: - `start` 預設為 0 - `step` 預設為 1 ```python # 使用 range() 生成數字序列 for i in range(5): # 相當於 range(0, 5, 1) print(i) # 輸出: 0 1 2 3 4 for i in range(2, 5): # 相當於 range(2, 5, 1) print(i) # 輸出: 2 3 4 for i in range(1, 10, 2): print(i) # 輸出: 1 3 5 7 9 for i in range(5, -1, -1): print(i) # 輸出: 5 4 3 2 1 0 ``` - 與`range()`相似的,就是前面提到的`list[a:b:c]` `a, b, c` 等同於 `start, stop, step`,只不過是以**索引值**取得部分陣列 - 有兩種形式`[a:b]`、`[a:b:c]` - `a`為空就是預設`0` - `b`為空就是預設陣列最後一個 - `c`不填時用第一種形式`[a:b]`,預設為`1` ```python arr = [10, 20, 30, 40, 50, 60] print(arr[:3]) # 輸出 [10, 20, 30] print(arr[2:]) # 輸出 [30, 40, 50, 60] print(arr[1:4]) # 輸出 [20, 30, 40] print(arr[::2]) # 輸出 [10, 30, 50] ``` - **while 迴圈**:當條件為 True 時重複執行。 ```python while 條件: # 執行的程式碼 ``` - 範例: ```python count = 0 while count < 3: print(count) count += 1 ``` - **break** 和 **continue**:迴圈控制 - **break**: 當遇到`break`的時候,會立即跳出所在迴圈 ```python= for i in range(5): print(i) for j in range(10): print(i, j) if j % 3 == 2: break # 直接跳出j迴圈,並接續執行i迴圈 ``` - **continue**: 遇到則跳過此回合,但繼續執行所在迴圈 ```python= i = 0 while i < 10: i += 1 if i == 3: continue print(i) ``` ### 範例練習 ```python # 使用 for 迴圈遍歷列表 fruits = ["apple", "banana", "cherry"] for fruit in fruits: print(fruit) # 使用 while 迴圈計算 1 到 5 的總和 sum = 0 num = 1 while num <= 5: sum += num num += 1 print("總和:", sum) ``` ### 牛刀小試 1. 輸入`n`,使用 `for` 迴圈計算 `1` 到 `n` 的總和 2. 輸入一個數字`n`,重複以下操作: - 如果他是奇數就將他乘上3再加1 - 如果是偶數就將他除以2 - 持續操作並輸出過程的n值,**直到**他變成1 --- ## 單元 8:函數(Function) ### 函數的定義與使用 - 函數是一組執行特定任務的程式碼,可以重複使用 當函數執行到 `return` 時,會立即跳出函數不再執行,並回傳後面接著的值 - 使用 `def` 關鍵字定義函數。 ```python def 函數名稱(參數): # 函數內的程式碼 ... return 返回值 # 呼叫函數 函數名稱(參數) ``` - **範例 1**: ```python def add_numbers(a, b): return a + b result = add_numbers(5, 3) # 以位置參數帶入 # result 變數會接住函數回傳的值 print(result) # 輸出: 8 result = add_numbers(b=5, a=3) # 以關鍵字參數帶入 # 呼叫進入函數後就會用指定參數名稱帶入,而非按照順序 ``` - 函數也可以不用有回傳值,會在函數執行完畢後自動返回 `None` - **範例 2**: ```python def say_hi(name): print(f"Hi {name}! How are you?") # 輸出: "Hi Eleven! How are you?" result = say_hi("Eleven") print(result) # 輸出: None ``` ### 範例練習 ```python # 練習 1: 定義函數計算平方 def square(num): return num ** 2 print(square(4)) # 輸出: 16 # 練習 2: 定義函數判斷是否為偶數 def is_even(num): if num % 2 == 0: return True else: return False print(is_even(10)) # 輸出: True print(is_even(7)) # 輸出: False ``` ### 牛刀小試 1. 定義一個函數 `fact(n)`,計算 $n!$ (階乘) $$ \begin{align} n! &= 1 \times 2 \times 3 \times ... \times n \\ ex.\ 4! &= 1 \times 2 \times 3 \times 4 = 24 \end{align} $$ - 定義 $0! = 1$ --- ### 函數進階用法 - **lambda 函數(匿名函數)**: - 使用 `lambda` 關鍵字定義一個簡單的匿名函數。 - 語法: ```python lambda 參數1, 參數2, ... : 表達式 ``` - 適用於需要簡單函數的場合。 ```python # 使用 lambda 函數計算平方 square = lambda x: x ** 2 print(square(5)) # 輸出: 25 # 使用 lambda 函數實現加法 add = lambda a, b: a + b print(add(3, 7)) # 輸出: 10 ``` - 或是將函數當作參數輸入也很方便 ```py arr = [1, 2, 3, 4, 5] arr2 = list(map(lambda x:x**2, arr)) print(arr2) # 輸出:[1, 4, 9, 16, 25] ``` - **特殊參數傳入**: - **\*args** - `*args` 把「多出來的位置參數」打包成一個 **tuple** - `args` 只是慣例命名,你也可以改成別的名字,但通常都用 `args` ```python def my_sum(*args): # args 是 tuple total = 0 for x in args: total += x return total print(my_sum(1, 2, 3)) # 輸出: 6 ``` - **\*\*kwargs** - `**kwargs` 會把「多出來的關鍵字參數」打包成一個 **dict**(字典) - `kwargs` 也是慣例命名(keyword arguments) ```python def show_profile(**kwargs): # kwargs 是 dict print(kwargs) show_profile(name="Alice", age=20, city="Taichung") # 輸出: {'name': 'Alice', 'age': 20, 'city': 'Taichung'} # 變成一個字典 ``` ### 牛刀小試 1. 定義一個函數 `my_max(*args)`,可以接收任意多個數字並回傳最大值 2. 定義一個函數 `print_info(**kwargs)`,把所有資訊用「一行一個 key: value」印出來 例如呼叫: ```python print_info(name = "Walter White", age = 52, job = "Math teacher") ``` 輸出: ``` name: Walter White age: 52 job: Math teacher ``` --- ### 遞迴函數 - 遞迴(Recursion) 指的是「函數在定義中**呼叫自己**」 - 常用於可被拆解為「**規模更小、結構相同問題**」的情境 - 遞迴通常包含兩個部分: - 終止條件(Base Case):什麼時候停止遞迴 - 遞迴呼叫(Recursive Case):問題如何縮小並再次呼叫自己 - 每個遞迴函數一定要有**終止條件**(base case),否則會造成無限呼叫,導致程式錯誤 - 範例:使用遞迴計算階乘(factorial) 利用 $n! = (n-1)! \times n$ 的觀念 $$ \begin{align} n! &= 1 \times 2 \times 3 \times ... \times (n-1) \times n \\ &= (1 \times 2 \times 3 \times ... \times (n-1)) \times n \\ &= (n-1)! \times n \end{align} $$ ```python def fact(n): if n == 0: # 終止條件 return 1 else: return n * fact(n - 1) # 呼叫自己(問題縮小) print(fact(3)) # 輸出: 6 ``` - 遞迴函數的呼叫階層圖 ``` fact(3) │ ├─ fact(2) * 3 │ │ │ ├─ fact(1) * 2 │ │ │ │ │ ├─ fact(0) * 1 │ │ │ │ │ │ │ └─ return 1 │ │ │ │ │ └─ return 1 * 1 = 1 │ │ │ └─ return 1 * 2 = 2 │ └─ return 2 * 3 = 6 ``` ### 牛刀小試 1. 定義一個函數 `fib(n)`,用遞迴的方式計算費氏數列,即 $$ \begin{equation} F_n = \begin{cases} 0, & n = 0 \\[6pt] 1, & n = 1 \\[6pt] F_{n-1} + F_{n-2}, & n \ge 2 \end{cases} \end{equation} $$ --- ## 單元 9:載入外部函數庫(Import) - python有許多別人已經做好的函數庫,裡面包含不只函數,也可能有定義好的常數可供使用 例如: - 數學計算 → `math` - 隨機抽樣 → `random` - 時間處理 → `time`, `datetime` - 科學計算 → `numpy` - 資料分析 → `pandas` - **from import as** - 利用`import 函數庫名稱` 就可以直接將函數庫導入,而函數庫就像一個資料夾,利用`.`來進入下一個資料夾或找到資料夾裡的函數 ```python import random # 導入函數庫 arr = [1, 3, 5, 7, 9, 11] # random函數庫中的幾個函數 a = random.random() # # 隨機產生[0, 1)中的一個浮點數 b = random.randint(1, 10) # 隨機產生[1, 10]中的一個整數 c = random.choice(arr) # 隨機取得arr中的一個元素 print(a, b, c) random.shuffle(arr) # 將arr內容打亂 print(arr) ``` random函數庫的詳細說明文件: [點這裡](https://docs.python.org/zh-tw/3.13/library/random.html) - 如果只要用特定的幾個函數,可以用`from 函數庫 import 內容`的形式 避免載入太多用不到的函數 ```python from random import randint, random # 可用逗號區隔多個函數、類別 print(random()) print(randint(1, 10)) # 就不用加random.了 ``` - `as` 可以幫模組取「簡短好打的名字」 ```python import math as m print(m.sqrt(25)) print(m.pi) ``` ### 牛刀小試 1. 搜尋網路,利用`math`函數庫,計算`1234567890`與`9876543210`的最大公因數(GCD) 2. 搜尋網路,利用`datetime`函數庫,印出當下日期與時間 --- ## 單元 10:類別(class) ### 為什麼要有class? - 想像今天如果我要儲存四個學生的名字、年齡、身高、體重,有什麼方法? - **方法1** ```python nameA, nameB, nameC, nameD = "Mike", "Dustin", "Lucas", "Will" ageA, ageB, ageC, ageD = 13, 12, 12, 13 heightA, heightB, heightC, heightD = 131, 129, 134, 127 weightA, weightB, weightC, weightD = 47, 53, 42, 38 ``` 超多變數,非常雜亂 所以聰明的你想到了方法2,使用**陣列**儲存 - **方法2** ```python name = ["Mike", "Dustin", "Lucas", "Will"] age = [13, 12, 12, 13] height = [131, 129, 134, 127] weight = [47, 53, 42, 38] ``` 但這樣同一個學生的資訊分散在四個陣列,難以看出關係 聰明的你又想到了方法3,可以用**字典**儲存 - **方法3** ```python A = {"name": "Mike", "age": 13, "height": 131, "weight": 47} B = {"name": "Dustin", "age": 12, "height": 129, "weight": 53} C = {"name": "Lucas", "age": 12, "height": 134, "weight": 42} D = {"name": "Will", "age": 13, "height": 127, "weight": 38} ``` 不過這樣每次都要定義一個結構一模一樣的字典結構,會不會太麻煩? --- ### class定義 - 將**結構定義**跟**資料**分開,先做好模板,再做出成品 ```python class Student: def __init__(self, name, age, height, weight): self.name = name self.age = age self.height = height self.weight = weight ``` - class 是一個模板,稱為**類別** 定義一個學生有`name`, `age`, `height`, `weight` 四種**屬性** - 而實際應用這個模板所產生的物件,稱為**實例(instance)** ```python! A = Student("Mike", 13, 131, 47) B = Student("Dustin", 12, 129, 53) C = Student("Lucas", 12, 134, 42) D = Student("Will", 13, 127, 38) ``` --- ### class 基本語法 - **最基本的 class 定義** ```python class 類別名稱: 類別內容 ``` - 類別名稱慣例使用 **大寫開頭** - `Student` ✅ - `student` ❌ - **`__init__`:初始化函數(建構子)** - 當你「建立一個物件」時,`__init__` 會自動被呼叫 - 用來設定這個物件一開始該有哪些屬性 ```python class Student: def __init__(self, name, age): self.name = name self.age = age ``` - `__init__` 不是自己呼叫的 而是在建立實例時自動執行: ```python s = Student("Mike", 13) # 呼叫 __init__() 函數並帶入參數 "Mike", 13 ``` - **存取物件的屬性** ```python print(s.name) # 輸出: Mike print(s.age) # 輸出: 13 ``` - **修改屬性** ```python s.age = 14 print(s.age) # 輸出: 14 ``` ### 方法(methods) - 方法就是「寫在 class 裡的函數」 - 第一個參數一定是 `self` ```python! class Student: def __init__(self, name): self.name = name # 定義一個方法,使Student可以說話 def say(self, content): print(f"{self.name}: {content}") ``` - 呼叫**方法**與呼叫函數很像,都要加上括號: `(參數)` ```python s = Student("Will") s.say("How are you?") # 輸出:"Will: How are you?" ``` - **什麼是 `self`?** - `self` 代表「**目前這個物件自己**」 - 用來在class定義中,找到呼叫這個方法的物件**本身** - 呼叫方法時,Python會自動幫你帶入這個物件,傳入self ```python s.say("How are you?") ↓ s.say(s, "How are you?") # 實際上Python會幫你這樣寫 ↓ def say(self -> s, content -> "How are you?"): # 帶進函數之後 print(f"{s.name}: {"How are you?"}") ``` ### 牛刀小試 1. 請定義一個 `Dog` 類別,並完成以下需求: - `Dog` 有兩個屬性: - `name` - `age` - 在 `__init__` 中設定這兩個屬性 - 定義一個方法 `bark`,呼叫後會印出: ``` <狗的名字>: Woof! ``` **範例使用方式** ```python d = Dog("Buddy", 3) d.bark() ``` **預期輸出** ``` Buddy: Woof! ``` 2. 請延續你剛剛寫的 `Dog` 類別,加入一個方法 `grow_up`: - `grow_up` 被呼叫時,讓狗的年齡加 1 - 並印出目前年齡 **範例使用方式** ```python d = Dog("Buddy", 3) d.grow_up() d.grow_up() ``` **預期輸出** ``` Buddy is now 4 years old Buddy is now 5 years old ``` --- ### 繼承(Inheritance) - **為什麼要有繼承?** ```python! class Student: def __init__(self, name, age, score): self.name = name self.age = age self.score = score class Teacher: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary ``` - `Student`, `Teacher` 有重複的屬性 `name` 和 `age`,因為他們都是**人** - 所以有更結構化的寫法 ```python! class Person: def __init__(self, name, age): self.name = name self.age = age class Student(Person): def __init__(self, name, age, score): super().__init__(name, age) self.score = score class Teacher(Person): def __init__(self, name, age, salary): super().__init__(name, age) self.salary = salary ``` - `Student`和`Teacher`都會**繼承**`Person`的**屬性**以及**方法** - 在這裡 `Student`和`Teacher` 稱為**子類別**,`Person` 為**父類別** - 雖然 `Student`和`Teacher` 中沒有寫 `self.name`,但因為繼承,所以也有這個屬性 ```python! t = Teacher("Clarke", 49, 50000) # 對應 name, age, salary print(t.name) # 輸出:Clarke ``` - **什麼是 `super()`?** - `super()` 讓你可以找到「**父類別**」,尤其是被**覆蓋**掉的方法 ```python class Person: def __init__(self, name, age): self.name = name self.age = age def say_hi(self): print(f"Hi, I'm {self.name}") class Student(Person): def __init__(self, name, age, score): super().__init__(name, age) self.score = score def say_hi(self): print("I am a student") def super_say_hi(self): super().say_hi() s = Student("Mike", 11, 'A') # (因為Student覆蓋掉了Person定義的say_hi) s.say_hi() # 輸出:"I am a student" s.super_say_hi() # 輸出:"Hi, I'm Mike" ``` ### **牛刀小試** 請完成以下類別設計: - 建立一個父類別 `Vehicle` - 具有兩個屬性: - `color` - `tire`(輪胎數) - 定義一個方法 `describe()`,用來描述交通工具的基本資訊 - 建立以下子類別,**皆需繼承 `Vehicle`** - `Bike` - 額外屬性:`electric`(`bool`,是否為電動) - **輪胎數固定為 2(不能由外部傳入)** - `Car` - 額外屬性:`seats` - **輪胎數固定為 4** - `Ship` - 額外屬性:`capacity` - **輪胎數固定為 0** - **方法要求** - 父類別 `Vehicle` 定義 `describe()` 方法 - 每個子類別都要 **覆寫 `describe()`** - 子類別的 `describe()` **必須使用 `super()` 呼叫父類別的 `describe()`** - **範例程式** ```python class Vehicle: # 定義 __init__ def describe(self): print(f"Color: {self.color}, Tires: {self.tire}") class Bike(Vehicle): def __init__(self, color, electric): # 呼叫父類別 __init__,輪胎數固定為 2 def describe(self): # 先呼叫父類別 describe() # 再印出是否為電動腳踏車 class Car(Vehicle): def __init__(self, color, seats): # 呼叫父類別 __init__,輪胎數固定為 4 def describe(self): # 先呼叫父類別 describe() # 再印出座位數 class Ship(Vehicle): def __init__(self, color, capacity): # 呼叫父類別 __init__,輪胎數固定為 0 def describe(self): # 先呼叫父類別 describe() # 再印出容量 b = Bike("red", electric=True) c = Car("black", seats=5) s = Ship("white", capacity=3000) b.describe() c.describe() s.describe() ``` --- ### 預期輸出 ``` Color: red, Tires: 2 This is an electric bike Color: black, Tires: 4 This car has 5 seats Color: white, Tires: 0 This ship can carry 3000 tons ```