Python-函數
Python 中可以使用def
關鍵字來定義函數。
函數定義規則:
- 函數代碼塊以 def 關鍵詞開頭,后接函數標識符名稱和圓括號 ()。
- 任何傳入參數和自變量必須放在圓括號中間,圓括號之間可以用于定義參數。
- 函數的第一行語句可以選擇性地使用文檔字符串—用于存放函數說明。
- 函數內容以冒號 : 起始,并且縮進。
- return [表達式] 結束函數,選擇性地返回一個值給調用方,不帶表達式的 return 相當于返回 None。
def greet(name):"""這是一個簡單的問候函數"""print(f"Hello, {name}!")# 示例調用
greet("Alice") # 輸出: Hello, Alice!
函數的參數
寫一個函數,根據給出的三條邊的長度判斷是否可以構成三角形,如果可以構成三角形則返回True
,否則返回False
def make_judgement(a, b, c):"""判斷三條邊的長度能否構成三角形"""return a + b > c and b + c > a and a + c > bprint(make_judgement(1, 2, 3)) # False
print(make_judgement(4, 5, 6)) # True
如果不想按照從左到右的順序依次給出a
、b
、c
三個參數的值,也可以使用關鍵字參數,通過“參數名=參數值”的形式為函數傳入參數。
print(make_judgement(b=2, c=3, a=1)) # False
print(make_judgement(c=6, b=4, a=5)) # True
在定義函數時,我們可以在參數列表中用/
設置強制位置參數(positional-only arguments),用*
設置命名關鍵字參數。所謂強制位置參數,就是調用函數時只能按照參數位置來接收參數值的參數;而命名關鍵字參數只能通過“參數名=參數值”的方式來傳遞和接收參數
# /前面的參數是強制位置參數
def make_judgement(a, b, c, /):"""判斷三條邊的長度能否構成三角形"""return a + b > c and b + c > a and a + c > b# 下面的代碼會產生TypeError錯誤,錯誤信息提示“強制位置參數是不允許給出參數名的”
# TypeError: make_judgement() got some positional-only arguments passed as keyword arguments
# print(make_judgement(b=2, c=3, a=1))
說明:強制位置參數是 Python 3.8 引入的新特性,在使用低版本的 Python 解釋器時需要注意。
# *后面的參數是命名關鍵字參數
def make_judgement(*, a, b, c):"""判斷三條邊的長度能否構成三角形"""return a + b > c and b + c > a and a + c > b# 下面的代碼會產生TypeError錯誤,錯誤信息提示“函數沒有位置參數但卻給了3個位置參數”
# TypeError: make_judgement() takes 0 positional arguments but 3 were given
# print(make_judgement(1, 2, 3))
參數的默認值
Python 中允許函數的參數擁有默認值,帶默認值的參數必須放在不帶默認值的參數之后
from random import randrange# 定義搖色子的函數
# 函數的自變量(參數)n表示色子的個數,默認值為2
# 函數的因變量(返回值)表示搖n顆色子得到的點數
def roll_dice(n=2):total = 0for _ in range(n):total += randrange(1, 7)return total# 如果沒有指定參數,那么n使用默認值2,表示搖兩顆色子
print(roll_dice())
# 傳入參數3,變量n被賦值為3,表示搖三顆色子獲得點數
print(roll_dice(3))
def add(a=0, b=0, c=0):"""三個數相加求和"""return a + b + c# 調用add函數,沒有傳入參數,那么a、b、c都使用默認值0
print(add()) # 0
# 調用add函數,傳入一個參數,該參數賦值給變量a, 變量b和c使用默認值0
print(add(1)) # 1
# 調用add函數,傳入兩個參數,分別賦值給變量a和b,變量c使用默認值0
print(add(1, 2)) # 3
# 調用add函數,傳入三個參數,分別賦值給a、b、c三個變量
print(add(1, 2, 3)) # 6
可變參數
Python 語言中可以通過星號表達式語法讓函數支持可變參數。所謂可變參數指的是在調用函數時,可以向函數傳入0
個或任意多個參數。
# 用星號表達式來表示args可以接收0個或任意多個參數
# 調用函數時傳入的n個參數會組裝成一個n元組賦給args
# 如果一個參數都沒有傳入,那么args會是一個空元組
def add(*args):total = 0# 對保存可變參數的元組進行循環遍歷for val in args:# 對參數進行了類型檢查(數值型的才能求和)if type(val) in (int, float):total += valreturn total# 在調用add函數時可以傳入0個或任意多個參數
print(add()) # 0
print(add(1)) # 1
print(add(1, 2, 3)) # 6
print(add(1, 2, 'hello', 3.45, 6)) # 12.45
如果我們希望通過“參數名=參數值”的形式傳入若干個參數,具體有多少個參數也是不確定的,我們還可以給函數添加可變關鍵字參數,把傳入的關鍵字參數組裝到一個字典中,
# 參數列表中的**kwargs可以接收0個或任意多個關鍵字參數
# 調用函數時傳入的關鍵字參數會組裝成一個字典(參數名是字典中的鍵,參數值是字典中的值)
# 如果一個關鍵字參數都沒有傳入,那么kwargs會是一個空字典
def foo(*args, **kwargs):print(args)print(kwargs)foo(3, 2.1, True, name='測試', age=43, gpa=4.95)
用模塊管理函數
import
關鍵字導入指定的模塊再使用完全限定名(模塊名.函數名
)的調用方式
module1.py
def foo():print('hello, world!')
module2.py
def foo():print('goodbye, world!')
test.py
import module1
import module2# 用“模塊名.函數名”的方式(完全限定名)調用函數,
module1.foo() # hello, world!
module2.foo() # goodbye, world!
在導入模塊時,還可以使用as
關鍵字對模塊進行別名,這樣我們可以使用更為簡短的完全限定名。
test.pyimport module1 as m1
import module2 as m2m1.foo() # hello, world!
m2.foo() # goodbye, world!
標準庫中的模塊和函數
Python 標準庫中提供了大量的模塊和函數來簡化我們的開發工作,我們之前用過的random
模塊就為我們提供了生成隨機數和進行隨機抽樣的函數;而time
模塊則提供了和時間操作相關的函數;我們之前用到過的math
模塊中還包括了計算正弦、余弦、指數、對數等一系列的數學函數。隨著我們深入學習 Python 語言,我們還會用到更多的模塊和函數。
Python 標準庫中還有一類函數是不需要import
就能夠直接使用的,我們將其稱之為內置函數,這些內置函數不僅有用而且還很常用,下面的表格列出了一部分的內置函數。
# 內置函數綜合案例:學生成績管理系統def student_grade_system():"""學生成績管理系統演示各種內置函數的使用"""print("=== 學生成績管理系統 ===")# 1. abs - 計算成績差值的絕對值score1 = 85score2 = 92diff = abs(score1 - score2)print(f"\n1. 兩位學生成績差值的絕對值: {diff}")# 2. bin/hex/oct - 顯示學生ID的不同進制表示student_id = 123print(f"\n2. 學生ID {student_id} 的不同表示:")print(f"二進制: {bin(student_id)}")print(f"十六進制: {hex(student_id)}")print(f"八進制: {oct(student_id)}")# 3. chr/ord - 處理特殊字符grade_symbol = 'A'print(f"\n3. 等級符號 '{grade_symbol}' 的Unicode碼: {ord(grade_symbol)}")print(f"Unicode碼65對應的字符: {chr(65)}")# 4. input - 獲取學生姓名name = input("\n4. 請輸入學生姓名: ")# 5. len - 計算姓名長度print(f"姓名長度: {len(name)}")# 6. max/min/sum - 計算成績統計grades = [85, 92, 78, 90, 88]print(f"\n5. 成績統計 - 班級成績: {grades}")print(f"最高分: {max(grades)}")print(f"最低分: {min(grades)}")print(f"平均分: {sum(grades) / len(grades):.2f}")# 7. pow - 計算加權成績base_score = 80weight = 1.2weighted_score = pow(base_score, weight)print(f"\n6. 基礎分 {base_score} 的加權分(權重1.2): {weighted_score:.2f}")# 8. range - 生成學號序列print("\n7. 生成的學號序列:")for id in range(2023001, 2023006):print(id, end=' ')# 9. round - 四舍五入平均分avg = sum(grades) / len(grades)print(f"\n\n8. 精確到小數點后2位的平均分: {round(avg, 2)}")# 10. type - 檢查數據類型print("\n9. 數據類型檢查:")print(f"grades的類型: {type(grades)}")print(f"name的類型: {type(name)}")# 11. open - 寫入學生數據到文件print("\n10. 將學生數據寫入文件...")with open('student_data.txt', 'w', encoding='utf-8') as f:f.write(f"學生姓名: {name}\n")f.write(f"學生成績: {grades}\n")f.write(f"平均分: {avg:.2f}\n")print("數據已寫入student_data.txt文件")# 運行學生成績管理系統
student_grade_system()
函數應用實戰
例子1:隨機驗證碼
設計一個生成隨機驗證碼的函數,驗證碼由數字和英文大小寫字母構成,長度可以通過參數設置。
import random
import string# 生成一個隨機字符串 ,長度為10 ,由數字和字母組成
ALL_CHAR = string.digits + string.ascii_letters;def generate_code(*,code_len=4):"""生成指定長度的驗證碼:param code_len: 驗證碼的長度(默認4個字符):return: 由大小寫英文字母和數字構成的隨機驗證碼字符串random.choice(ALL_CHAR,k=code_len) 從ALL_CHAR中隨機選擇k個字符"""return ''.join(random.choices(ALL_CHAR,k=code_len))for _ in range(5):print(generate_code(code_len=10))
例子2:判斷素數
設計一個判斷給定的大于1的正整數是不是質數的函數。質數是只能被1和自身整除的正整數(大于1),如果一個大于 1 的正整數 N 是質數,那就意味著在 2 到 N?1 之間都沒有它的因子。
def is_prime(num: int) -> bool:"""判斷一個正整數是不是質數:param num: 大于1的正整數:return: 如果num是質數返回True,否則返回False"""for i in range(2, int(num ** 0.5) + 1):if num % i == 0:return Falsereturn True
說明1:上面is_prime
函數的參數num
后面的: int
用來標注參數的類型,雖然它對代碼的執行結果不產生任何影響,但是很好的增強了代碼的可讀性。同理,參數列表后面的-> bool
用來標注函數返回值的類型,它也不會對代碼的執行結果產生影響,但是卻讓我們清楚的知道,調用函數會得到一個布爾值,要么是True
,要么是False
。
說明2:上面的循環并不需要從 2 循環到 N?1 ,因為如果循環進行到 N 時,還沒有找到$\small{N}$的因子,那么 N 之后也不會出現 N 的因子,大家可以自己想一想這是為什么。
例子3:最大公約數和最小公倍數
設計計算兩個正整數最大公約數和最小公倍數的函數。 x 和 y 的最大公約數是能夠同時整除 x 和 y 的最大整數,如果 x 和 y 互質,那么它們的最大公約數為 1; x 和 y 的最小公倍數是能夠同時被 x 和 y 整除的最小正整數,如果 x 和 y 互質,那么它們的最小公倍數為 x×y 。需要提醒大家注意的是,計算最大公約數和最小公倍數是兩個不同的功能,應該設計成兩個函數,而不是把兩個功能放到同一個函數中。
def lcm(x: int, y: int) -> int:"""求最小公倍數"""return x * y // gcd(x, y)def gcd(x: int, y: int) -> int:"""求最大公約數"""while y % x != 0:x, y = y % x, xreturn x