National Taiwan University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

台灣大學 AQI 69 24°C PM2.5 18
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 13

函數設計

def、return 與呼叫堆疊;傳進函數的是什麼?
Interaction實作 30 minute(s)

Learning Objectives

  • 函數(function)的定義方式:def、參數、docstring
  • 如何呼叫函數,以及 return 回傳值(含沒有 return 時的 None)
  • 用呼叫堆疊看懂函數呼叫函數的執行順序
  • 函數傳引數的方式:pass by assignment

為什麼需要函數

寫程式時常常有一段功能要在很多地方用到:計算平均、檢查輸入、印出報表。 如果每次都複製貼上,改一個地方就要改很多份,也很容易漏改。

函數(function) 把一段程式碼包起來、取一個名字,需要時「呼叫」它就好:

  • 可重複使用(reusable):寫一次,到處用。
  • 模組化:把大問題拆成幾個小功能,每個函數只做一件事,程式更好讀、好測試。

你已經用過很多 Python 內建的函數,例如 print()、len()、max()。這一課要學的是自己寫的 使用者自訂函數(user-defined functions)。

def sum_nums(n1, n2):      # def 函數名稱(參數):
    """回傳兩個數字的和"""   # docstring:給使用者看的說明
    return n1 + n2         # return 結束函數並傳回結果

函數名稱依照 PEP 8 慣例使用英文小寫,多個單字用底線 _ 連接,例如 make_a_sound。

定義與呼叫函數

先注意第一步:執行 def 那一行時,函數內容並不會執行,只是建立一個函數物件,讓名稱 make_a_sound 指向它。之後每次呼叫,右邊會多出一個 make_a_sound 的框架(frame),執行完就消失。

註解與 docstring

函數下面第一行用三個雙引號包起來的字串,稱為 docstring(document string)。

# 註解 docstring """..."""
給誰看 寫程式的人自己 使用這個函數的人
能不能在程式裡取得 不能 可以:函數.__doc__、help(函數)
用在哪裡 任何一行 函數、模組、類別的開頭

參數與回傳值

呼叫時傳進去的值(引數,argument)會指派給函數定義的名稱(參數,parameter)。return 把結果交回呼叫的地方。看最後兩行:printme 沒有寫 return,所以呼叫結果是 None。

呼叫堆疊:函數呼叫函數

函數裡面可以再呼叫別的函數。Python 用呼叫堆疊(call stack)記住「現在在哪個函數裡、做完要回到哪裡」:

  1. 每呼叫一次函數,堆疊頂端就放上一個新框架(frame),裡面是這次呼叫自己的參數與區域變數。
  2. 函數 return 時,它的框架就被拿掉,執行回到下面那一層、剛才呼叫它的那一行。
  3. 同一個函數被呼叫兩次,就會有兩個(先後出現的)框架,彼此的變數互不干擾。

看框架出現與消失

這是本課最重要的動畫。一步一步按,眼睛盯著右邊的框架區:呼叫 sum_of_squares 時多出一層、它再呼叫 square 時變成三層;square 回傳後最上層消失,執行回到 sum_of_squares 的下一行。數數看 square 的框架一共出現了幾次。

用元組傳回多個值

return min(nums), max(nums) 其實是把兩個值打包成一個元組傳回;呼叫端可以用上一課的解包一次接住。

想一想:定義、呼叫與回傳

1.

下面的程式會印出什麼?

def add(a, b):
    print(a + b, end=" ")

r = add(2, 3)
print(r)
2.

下面的程式會印出什麼?

def f(x):
    return x + 1
    print("done")

print(f(f(1)))
3.

程式執行到 def square(x): 這一行時,會發生什麼事?

第 1 次作答

傳進函數的是什麼?Pass by assignment

Python 傳引數的方式稱為 pass by assignment:呼叫 changeme(myvar) 時,就像在函數裡執行了一次 參數 = 引數 的指派。參數和外面的變數指向同一個物件,但它們是兩個不同的名稱。

接下來兩個範例幾乎一模一樣,結果卻不同。關鍵在函數裡做的是哪一種事:

函數裡做的事 例子 外面看得到變化嗎
重新指派參數(讓名稱改指向別的物件) myvar = 50 看不到
修改參數指向的可變物件 mylist[2] = 50 看得到

重新指派整數參數

函數裡和外面都有一個叫 myvar 的名稱,但它們在不同的框架裡。第 4 行只讓函數框架裡的 myvar 改指向 50,模組框架裡的 myvar 仍然是 20。

修改串列參數

這次參數指向一個串列。看右邊的箭頭:函數框架的 mylist 和模組框架的 mylist 指向同一個串列物件。第 4 行修改的是那個物件本身,所以函數結束後,外面也看得到。

追蹤參數傳遞

1.

下面的程式會印出什麼?

def reset(nums):
    nums = [0, 0]

data = [5, 6]
reset(data)
print(data)
2.

下面的程式會印出什麼?

def grow(nums, n):
    nums.append(n)
    n = n * 10

data = [1]
k = 2
grow(data, k)
print(data, k)
第 1 次作答

實作:計算及格人數

完成函數 count_pass(scores):傳回串列 scores 中大於或等於 60 分的人數。記得用 return 傳回結果,而不是在函數裡 print。

你的輸出
預期輸出

重點精華回顧

  • 函數是一段可重複使用的程式碼;def 名稱(參數): 定義,名稱(引數) 呼叫。
  • 執行 def 只建立函數物件,不執行內容;呼叫時才執行。
  • docstring 寫在函數第一行,給使用者看,可用 __doc__ 或 help() 取得;# 註解是給自己看的。
  • return 結束函數並傳回值;沒有 return(或只寫 return)時傳回 None;用元組可以一次傳回多個值。
  • 每次呼叫都會在呼叫堆疊放上一個新框架,回傳後框架消失,執行回到呼叫的那一行。
  • Pass by assignment:參數和引數指向同一個物件。重新指派參數不影響外面;修改可變物件(串列)外面看得到。
這一課完成了!
Next lesson:函數的引數
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。