Post

Python 基礎:自訂函數的基礎介紹

Python 基礎:自訂函數的基礎介紹

Python 基礎:自訂函數的基礎介紹

在 Python 程式設計中,函數(Function)是一個非常重要的概念。無論你是初學者還是有一點基礎的開發者,掌握函數的運用都能讓你的程式碼更加簡潔、有效率且容易維護。本篇文章將帶你深入了解 Python 自訂函數的基礎,並且特別說明全域變數區域變數的差異,以及常見的錯誤與重點整理,幫助你建立扎實的函數概念。

1. 函數的基本概念

1.1 什麼是函數?

函數(Function)是一段可以重複使用的程式碼區塊,它接受輸入(參數),經過處理後產生輸出(回傳值)。你可以把函數想像是工廠裡的加工機器:把原材料放進去,經過特定處理後,得到我們想要的產品。

在 Python 中,函數主要分為兩大類:

  • 內建函數(Built-in Functions):Python 已經幫你寫好的函數,例如 len()max()min()print() 等。這些函數可以直接使用,無需額外定義。
  • 自訂函數(User-defined Functions):由程式設計師根據需求自行設計的函數。你可以根據要解決的問題,定義自己的函數來完成特定任務。

1.2 為什麼要使用函數?

使用函數的好處有很多:

  1. 程式碼重用:相同的邏輯只需要寫一次,之後可以無限次呼叫使用。
  2. 提高可讀性:將複雜的邏輯包裝成具有意義名稱的函數,讓程式更容易理解。
  3. 方便維護:當需要修改功能時,只需要修改函數內部的程式碼,不用到處尋找並修改。
  4. 模組化設計:將大型程式拆分成多個小模組,更容易開發與測試。

1.3 生活中的函數比喻

讓我們用生活中的例子來理解函數的概念:

把函數想像是「烤箱」:輸入食材(參數),經過烤箱的處理(函數內部的程式碼),最後得到美味的料理(回傳值)。

或者:

把函數想像是「自動販賣機」:投入硬幣(參數),選擇飲料(函數邏輯),最後掉出飲料(回傳結果)。

2. 自訂函數如何運作

2.1 函數的定義語法

在 Python 中,我們使用 def 關鍵字來定義一個函數。基本語法如下:

1
2
3
def 函數名稱(參數1, 參數2, ...):
    # 函數內部的程式碼
    return 回傳值

讓我們來看一個簡單的範例:

程式碼範例:攝氏轉華氏

1
2
3
def 攝氏轉華氏(C):
    F = 1.8 * C + 32
    return F

執行結果

  • 如果我們呼叫 攝氏轉華氏(50),會回傳 122(華氏溫度)。
  • 也就是說,當輸入 C=50 時,經過函數計算,輸出結果為 F=122。

2.2 函數的呼叫方式

要使用一個已經定義好的函數,稱為「呼叫函數」。呼叫語法如下:

1
結果 = 函數名稱(參數值)

例如:

1
2
3
4
5
6
7
def 攝氏轉華氏(C):
    F = 1.8 * C + 32
    return F

# 呼叫函數
結果 = 攝氏轉華氏(50)
print(結果)  # 輸出:122

2.3 參數的傳遞方式

Python 中的參數傳遞是採用「物件參考傳遞」的方式。簡單來說:

  • 當你傳遞不可變物件(如數字、字串、元組)時,相當於「傳值」。
  • 當你傳遞可變物件(如列表、字典)時,相當於「傳參考」。

這點在設計函數時需要特別注意,否則可能會意外修改到原始資料。

3. 有回傳和不回傳的函數

3.1 有回傳值的函數

使用 return 關鍵字可以將處理結果回傳給呼叫者。函數一旦執行到 return,就會立即結束,並回傳指定的值。

1
2
3
4
5
6
def 加法(a, b):
    結果 = a + b
    return 結果

總和 = 加法(10, 20)
print(總和)  # 輸出:30

3.2 不回傳結果的函數

有些函數只負責執行某些操作,但不需要回傳結果。這類函數通常用於顯示資訊、修改資料結構或產生副作用。

範例:不回傳結果的函數

1
2
3
4
5
def 打招呼(name):
    print("哈囉," + name)

# 呼叫函數
打招呼("宅爸")  # 輸出:哈囉,宅爸

注意:雖然這個函數沒有使用 return,但它仍然完成了「打招呼」這個任務。在 Python 中,沒有回傳值的函數預設會回傳 None

3.3 return 與 print 的區別

很多初學者會混淆 returnprint

  • print():將結果「顯示」在螢幕上,但不會將結果傳遞給程式其他地方使用。
  • return:將結果「回傳」給呼叫者,可以進一步處理或儲存。
1
2
3
4
5
6
7
8
def 範例1():
    print("Hello")  # 只會顯示,不會回傳

def 範例2():
    return "Hello"  # 回傳字串,可以存到變數

結果 = 範例2()
print(結果)  # 可以使用回傳的值

4. 全域變數與區域變數

這是 Python 函數中非常重要但常被忽略的概念。正確理解這兩種變數的差異,可以避免很多程式碼的錯誤。

4.1 什麼是區域變數(Local Variable)?

區域變數是指在函數內部定義的變數,只能在該函數內部存取。當函數執行完畢後,這些變數就會被銷毀,無法從函數外部存取。

1
2
3
4
5
6
7
8
9
def 測試區域變數():
    x = 10  # 這是區域變數
    print("函數內部:", x)

# 呼叫函數
測試區域變數()  # 輸出:函數內部:10

# 嘗試在函數外部存取 x
print(x)  # 會發生 NameError: name 'x' is not defined

4.2 什麼是全域變數(Global Variable)?

全域變數是指在函數外部定義的變數,可以在程式的任何位置存取(無論是在函數內部還是外部)。

1
2
3
4
5
6
7
8
9
10
y = 20  # 這是全域變數

def 測試全域變數():
    print("函數內部:", y)  # 可以存取全域變數

# 呼叫函數
測試全域變數()  # 輸出:函數內部:20

# 在函數外部也可以存取
print("函數外部:", y)  # 輸出:函數外部:20

4.3 在函數內部修改全域變數

如果你想在函數內部修改全域變數,需要使用 global 關鍵字宣告。

1
2
3
4
5
6
7
8
9
10
11
12
count = 0  # 全域變數

def 增加計數():
    global count  # 宣告我要使用全域變數 count
    count = count + 1
    print("函數內部:", count)

# 呼叫函數
增加計數()  # 輸出:函數內部:1
增加計數()  # 輸出:函數內部:2

print("函數外部:", count)  # 輸出:函數外部:2

如果沒有使用 global,Python 會把 count = count + 1 視為建立一個新的區域變數,導致錯誤:

1
2
3
4
5
6
count = 0

def 錯誤的範例():
    count = count + 1  # 會發生錯誤!

錯誤的範例()  # UnboundLocalError: local variable 'count' referenced before assignment

4.4 全域變數與區域變數的比較

特性區域變數全域變數
定義位置函數內部函數外部
存取範圍只能在函數內部整個程式檔案
生命週期函數執行期間程式執行期間
記憶體較少(函數結束後釋放)較多(直到程式結束)
安全性較高(不會被意外修改)較低(可能被多處修改)

4.5 建議:盡量使用區域變數

一般來說,建議盡量使用區域變數,因為:

  1. 減少副作用:避免函數意外修改外部狀態,造成難以追蹤的 bug。
  2. 提高可讀性:函數的輸入輸出更加明確。
  3. 便於測試:每個函數是獨立的單元,更容易單元測試。
  4. 執行效率:全域變數的查找比區域變數慢一些。

如果確實需要跨函數共享資料,可以使用參數傳遞回傳值的方式,這是更好的設計模式。

5. 常見錯誤與解決方案

5.1 忘記 return 或 return 位置錯誤

錯誤範例

1
2
3
4
5
6
def 求平均值(a, b):
    平均 = (a + b) / 2
    # 忘記寫 return

結果 = 求平均值(10, 20)
print(結果)  # 輸出:None

解決方案:確認函數需要回傳值,並正確使用 return

5.2 在函數內部修改全域變數(未使用 global)

錯誤範例

1
2
3
4
5
6
total = 100

def 購物():
    total = total - 50  # 沒有宣告 global

購物()  # UnboundLocalError

解決方案:如果要修改全域變數,記得使用 global 關鍵字;或者改用參數傳遞和回傳值。

5.3 參數名稱與全域變數名稱衝突

錯誤範例

1
2
3
4
5
6
7
8
name = "小明"

def 打招呼(name):
    print("你好," + name)

打招呼("小華")  # 輸出:你好,小華
# 這裡其實是正常的,因為參數會覆蓋區域範圍內的 name
# 但如果沒有傳遞參數,就會出問題

解決方案:注意參數命名,避免與全域變數混淆。如果需要使用全域變數,明確用 global 宣告。

5.4 函數名稱重複定義

錯誤範例

1
2
3
4
5
6
7
def 加法(a, b):
    return a + b

def 加法(a, b, c):  # 與上面同名,會覆蓋前面的定義
    return a + b + c

print(加法(1, 2))  # TypeError: 加法() missing 1 required positional argument

解決方案:確保每個函數有獨特的名稱,或使用不同的參數數量(但這不是好的設計)。

5.5 預設參數使用可變物件

錯誤範例

1
2
3
4
5
6
7
8
def 添加到列表(項目, 列表=[]):
    列表.append(項目)
    return 列表

結果1 = 添加到列表("蘋果")
結果2 = 添加到列表("香蕉")
print(結果1)  # ['蘋果', '香蕉']
print(結果2)  # ['蘋果', '香蕉']  # 預期應該只有 '香蕉'

解決方案:不要使用可變物件作為預設參數,改用 None

1
2
3
4
5
def 添加到列表(項目, 列表=None):
    if 列表 is None:
        列表 = []
    列表.append(項目)
    return 列表

6. 練習與應用

6.1 練習:判斷閏年

閏年的判斷規則:

  • 西元年份除以 4 餘數為 0,且除以 100 餘數不為 0
  • 或者西元年份除以 400 餘數為 0

程式碼範例

1
2
3
4
5
6
7
8
def 判斷閏年(year):
    return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0

# 測試
print(判斷閏年(2020))  # True(2020 是閏年)
print(判斷閏年(2021))  # False(2021 不是閏年)
print(判斷閏年(2000))  # True(2000 是閏年)
print(判斷閏年(1900))  # False(1900 不是閏年)

6.2 練習:判斷質數

質數是指只能被 1 和本身整除的大於 1 的整數。

程式碼範例

1
2
3
4
5
6
7
8
9
10
11
12
def 判斷質數(n):
    if n < 2:
        return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return False
    return True

# 測試
print(判斷質數(7))   # True
print(判斷質數(10))  # False
print(判斷質數(17))  # True

6.3 練習:計算階乘

程式碼範例

1
2
3
4
5
6
7
8
9
10
def 計算階乘(n):
    if n < 0:
        return None  # 負數沒有階乘
    if n == 0 or n == 1:
        return 1
    return n * 計算階乘(n - 1)

# 測試
print(計算階乘(5))  # 120
print(計算階乘(0))  # 1

6.4 練習:使用區域變數與全域變數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 全域變數
price = 1000

def 計算含稅價格():
    稅率 = 0.05  # 區域變數
    稅金 = price * 稅率  # 使用全域變數 price
    總價 = price + 稅金
    return 總價

def 計算折扣價格(折扣):
    折扣率 = 折扣 / 100  # 區域變數
    折後價 = price * (1 - 折扣率)
    return 折後價

print(計算含稅價格())    # 輸出:1050.0
print(計算折扣價格(20))  # 輸出:800.0
print(稅率)              # 會錯誤:NameError

7. 重點整理

7.1 函數的核心概念

  1. 函數定義:使用 def 關鍵字定義函數。
  2. 參數傳遞:函數可以接受零個或多個參數。
  3. 回傳值:使用 return 關鍵字回傳結果,沒有回傳則預設回傳 None
  4. 函數呼叫:透過 函數名稱() 來執行函數。

7.2 全域變數與區域變數

  • 區域變數:在函數內部定義,只能在函數內部使用,函數結束後會被銷毀。
  • 全域變數:在函數外部定義,可以在整個程式檔案中使用。
  • 修改全域變數:在函數內部使用 global 關鍵字來宣告。
  • 最佳實踐:盡量使用區域變數,減少使用全域變數。

7.3 常見錯誤提醒

  1. 忘記使用 return 回傳結果
  2. 在函數內部修改全域變數時未使用 global
  3. 參數名稱與全域變數名稱衝突
  4. 使用可變物件作為函數的預設參數

7.4 為什麼要使用函數?

  • 提高程式碼的重用性
  • 增加程式的可讀性
  • 方便程式的維護與測試
  • 實現模組化的程式設計

7.5 學習建議

  1. 從簡單開始:先從小的、簡單的函數開始練習。
  2. 命名有意義:函數名稱應該清楚表達它的功能。
  3. 保持簡潔:每個函數盡量只做一件事。
  4. 多多練習:將日常生活中的問題轉換成函數來解決。
  5. 重構舊程式:嘗試把之前寫的程式碼改寫成函數的形式。

自訂函數是 Python 程式設計中不可或缺的基礎概念。透過函數,我們可以將複雜的問題拆分為多個小問題,讓程式碼更加結構化、易於理解和維護。

8. 換你動手做做看囉

程式設計最重要的是動手實作!快來挑戰看看:

  • 步驟 1:連到解題網:http://163.30.43.15/
  • 步驟 2:註冊帳號且登入,加入課程。
    • 課程代碼SzNTKb (若已註冊過就不需再註冊)
  • 步驟 3:練習完成以下作業:
    1. 第 9 個作業:YouTube 線上課程 - 課堂作業 自訂函數

加油!寫程式就是不斷試錯與成長的過程。有任何問題歡迎在下方留言!

This post is licensed under CC BY 4.0 by the author.

© homedad. Some rights reserved.

Using the Chirpy theme for Jekyll.