第5章 函數¶
函數是Python編程中最重要的概念之一,它允許我們將代碼組織成可重用的模塊,提高代碼的可讀性和維護性。本章將深入講解Python函數的各個方面,從基礎的函數定義到高級的遞歸和內置函數使用。
本系列文章所使用到的示例源碼:Python從入門到精通示例代碼
5.1 函數的定義與調用¶
函數的概念與作用¶
函數是一段具有特定功能的可重用代碼塊。使用函數的主要優勢包括:
- 代碼複用:避免重複編寫相同的代碼
- 模塊化編程:將複雜問題分解爲簡單的子問題
- 提高代碼可讀性:使程序結構更清晰
函數的定義語法¶
在Python中,使用def關鍵字定義函數:
def function_name(parameters):
"""文檔字符串(可選)"""
# 函數體
return value # 返回值(可選)
讓我們看一個簡單的例子:
def greet(name):
"""問候函數,接收一個姓名參數"""
return f"Hello, {name}!"
# 調用函數
message = greet("Alice")
print(message)
輸出結果:
Hello, Alice!
函數命名規範¶
函數名應該遵循以下規範:
- 使用小寫字母和下劃線
- 名稱應該描述函數的功能
- 避免使用Python關鍵字
# 好的函數名
def calculate_area(radius):
return 3.14159 * radius ** 2
def get_user_input():
return input("請輸入內容: ")
# 不好的函數名
def func1(): # 名稱不夠描述性
pass
def Class(): # 應該用於類名,不是函數名
pass
函數的文檔字符串¶
文檔字符串(docstring)用於描述函數的功能、參數和返回值:
def calculate_bmi(weight, height):
"""
計算身體質量指數(BMI)
參數:
weight (float): 體重,單位爲千克
height (float): 身高,單位爲米
返回:
float: BMI值
"""
return weight / (height ** 2)
# 使用help()函數查看文檔
help(calculate_bmi)
輸出結果:
Help on function calculate_bmi in module __main__:
calculate_bmi(weight, height)
計算身體質量指數(BMI)
參數:
weight (float): 體重,單位爲千克
height (float): 身高,單位爲米
返回:
float: BMI值
5.2 參數傳遞¶
位置參數¶
位置參數是最基本的參數類型,參數的值按照定義時的順序傳遞:
def introduce_person(name, age, city):
"""介紹一個人的基本信息"""
return f"我叫{name},今年{age}歲,來自{city}"
# 按位置傳遞參數
result = introduce_person("張三", 25, "北京")
print(result)
輸出結果:
我叫張三,今年25歲,來自北京
關鍵字參數¶
關鍵字參數允許通過參數名來指定值,提高代碼的可讀性:
def create_user_profile(name, age, email, city="未知"):
"""創建用戶檔案"""
profile = {
"姓名": name,
"年齡": age,
"郵箱": email,
"城市": city
}
return profile
# 使用關鍵字參數
user1 = create_user_profile(name="李四", email="lisi@example.com", age=30)
print(user1)
# 混合使用位置參數和關鍵字參數
user2 = create_user_profile("王五", age=28, email="wangwu@example.com", city="上海")
print(user2)
輸出結果:
{'姓名': '李四', '年齡': 30, '郵箱': 'lisi@example.com', '城市': '未知'}
{'姓名': '王五', '年齡': 28, '郵箱': 'wangwu@example.com', '城市': '上海'}
默認參數¶
默認參數爲函數參數提供默認值,使函數調用更加靈活:
def power(base, exponent=2):
"""計算冪運算,默認計算平方"""
return base ** exponent
# 使用默認參數
print(f"5的平方: {power(5)}")
print(f"2的3次方: {power(2, 3)}")
print(f"10的4次方: {power(base=10, exponent=4)}")
輸出結果:
5的平方: 25
2的3次方: 8
10的4次方: 10000
注意:默認參數的陷阱
使用可變對象作爲默認參數時要特別小心:
# 錯誤的做法
def add_item_wrong(item, target_list=[]):
target_list.append(item)
return target_list
# 正確的做法
def add_item_correct(item, target_list=None):
if target_list is None:
target_list = []
target_list.append(item)
return target_list
# 演示錯誤做法的問題
list1 = add_item_wrong("apple")
list2 = add_item_wrong("banana")
print(f"list1: {list1}")
print(f"list2: {list2}")
# 演示正確做法
list3 = add_item_correct("apple")
list4 = add_item_correct("banana")
print(f"list3: {list3}")
print(f"list4: {list4}")
輸出結果:
list1: ['apple', 'banana']
list2: ['apple', 'banana']
list3: ['apple']
list4: ['banana']
5.3 可變參數¶
*args(可變位置參數)¶
*args允許函數接收任意數量的位置參數:
def calculate_sum(*numbers):
"""計算任意數量數字的和"""
total = 0
for num in numbers:
total += num
return total
# 傳遞不同數量的參數
print(f"1個數字的和: {calculate_sum(5)}")
print(f"3個數字的和: {calculate_sum(1, 2, 3)}")
print(f"5個數字的和: {calculate_sum(10, 20, 30, 40, 50)}")
# 使用列表解包
numbers_list = [1, 2, 3, 4, 5]
print(f"列表解包的和: {calculate_sum(*numbers_list)}")
輸出結果:
1個數字的和: 5
3個數字的和: 6
5個數字的和: 150
列表解包的和: 15
**kwargs(可變關鍵字參數)¶
**kwargs允許函數接收任意數量的關鍵字參數:
def create_student_info(name, **details):
"""創建學生信息,接收任意額外的詳細信息"""
info = {"姓名": name}
info.update(details)
return info
# 傳遞不同的關鍵字參數
student1 = create_student_info("張三", age=20, major="計算機科學")
student2 = create_student_info("李四", age=19, major="數學", grade="大二", gpa=3.8)
print(f"學生1信息: {student1}")
print(f"學生2信息: {student2}")
# 使用字典解包
extra_info = {"city": "北京", "hobby": "編程"}
student3 = create_student_info("王五", age=21, **extra_info)
print(f"學生3信息: {student3}")
輸出結果:
學生1信息: {'姓名': '張三', 'age': 20, 'major': '計算機科學'}
學生2信息: {'姓名': '李四', 'age': 19, 'major': '數學', 'grade': '大二', 'gpa': 3.8}
學生3信息: {'姓名': '王五', 'age': 21, 'city': '北京', 'hobby': '編程'}
參數的完整語法順序¶
函數參數的完整順序必須是:位置參數 → 默認參數 → args → *kwargs
def complete_function(pos_arg, default_arg="default", *args, **kwargs):
"""演示完整的參數語法"""
print(f"位置參數: {pos_arg}")
print(f"默認參數: {default_arg}")
print(f"可變位置參數: {args}")
print(f"可變關鍵字參數: {kwargs}")
print("-" * 30)
# 各種調用方式
complete_function("必需參數")
complete_function("必需參數", "自定義默認值")
complete_function("必需參數", "自定義默認值", 1, 2, 3)
complete_function("必需參數", "自定義默認值", 1, 2, 3, key1="value1", key2="value2")
輸出結果:
位置參數: 必需參數
默認參數: default
可變位置參數: ()
可變關鍵字參數: {}
------------------------------
位置參數: 必需參數
默認參數: 自定義默認值
可變位置參數: ()
可變關鍵字參數: {}
------------------------------
位置參數: 必需參數
默認參數: 自定義默認值
可變位置參數: (1, 2, 3)
可變關鍵字參數: {}
------------------------------
位置參數: 必需參數
默認參數: 自定義默認值
可變位置參數: (1, 2, 3)
可變關鍵字參數: {'key1': 'value1', 'key2': 'value2'}
------------------------------
5.4 返回值與作用域¶
函數的返回值¶
單個返回值¶
def get_circle_area(radius):
"""計算圓的面積"""
return 3.14159 * radius ** 2
area = get_circle_area(5)
print(f"半徑爲5的圓的面積: {area}")
輸出結果:
半徑爲5的圓的面積: 78.53975
多個返回值¶
def get_rectangle_info(length, width):
"""計算矩形的面積和周長"""
area = length * width
perimeter = 2 * (length + width)
return area, perimeter # 返回元組
# 接收多個返回值
rect_area, rect_perimeter = get_rectangle_info(10, 5)
print(f"矩形面積: {rect_area}")
print(f"矩形周長: {rect_perimeter}")
# 也可以作爲元組接收
result = get_rectangle_info(8, 6)
print(f"結果元組: {result}")
print(f"面積: {result[0]}, 周長: {result[1]}")
輸出結果:
矩形面積: 50
矩形周長: 30
結果元組: (48, 28)
面積: 48, 周長: 28
無返回值(None)¶
def print_user_info(name, age):
"""打印用戶信息,無返回值"""
print(f"用戶姓名: {name}")
print(f"用戶年齡: {age}")
# 沒有return語句,默認返回None
result = print_user_info("張三", 25)
print(f"函數返回值: {result}")
輸出結果:
用戶姓名: 張三
用戶年齡: 25
函數返回值: None
變量的作用域¶
局部作用域和全局作用域¶
# 全局變量
global_var = "我是全局變量"
def scope_demo():
# 局部變量
local_var = "我是局部變量"
print(f"函數內部訪問全局變量: {global_var}")
print(f"函數內部訪問局部變量: {local_var}")
scope_demo()
print(f"函數外部訪問全局變量: {global_var}")
# print(local_var) # 這會報錯,因爲局部變量在函數外部不可訪問
輸出結果:
函數內部訪問全局變量: 我是全局變量
函數內部訪問局部變量: 我是局部變量
函數外部訪問全局變量: 我是全局變量
global關鍵字¶
counter = 0 # 全局變量
def increment_counter():
global counter # 聲明使用全局變量
counter += 1
print(f"計數器值: {counter}")
def reset_counter():
global counter
counter = 0
print("計數器已重置")
print(f"初始計數器值: {counter}")
increment_counter()
increment_counter()
increment_counter()
reset_counter()
print(f"最終計數器值: {counter}")
輸出結果:
初始計數器值: 0
計數器值: 1
計數器值: 2
計數器值: 3
計數器已重置
最終計數器值: 0
nonlocal關鍵字¶
def outer_function():
x = "外層函數的變量"
def inner_function():
nonlocal x # 聲明使用外層函數的變量
x = "被內層函數修改的變量"
print(f"內層函數中的x: {x}")
print(f"調用內層函數前的x: {x}")
inner_function()
print(f"調用內層函數後的x: {x}")
outer_function()
輸出結果:
調用內層函數前的x: 外層函數的變量
內層函數中的x: 被內層函數修改的變量
調用內層函數後的x: 被內層函數修改的變量
5.5 遞歸函數¶
遞歸的概念¶
遞歸是指函數調用自身的編程技術。每個遞歸函數都必須包含:
- 基礎情況:遞歸的終止條件
- 遞歸情況:函數調用自身的情況
經典遞歸示例¶
階乘計算¶
def factorial(n):
"""計算n的階乘"""
# 基礎情況
if n == 0 or n == 1:
return 1
# 遞歸情況
else:
return n * factorial(n - 1)
# 測試階乘函數
for i in range(6):
result = factorial(i)
print(f"{i}! = {result}")
輸出結果:
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
斐波那契數列¶
def fibonacci(n):
"""計算斐波那契數列的第n項"""
# 基礎情況
if n <= 1:
return n
# 遞歸情況
else:
return fibonacci(n - 1) + fibonacci(n - 2)
# 生成斐波那契數列的前10項
print("斐波那契數列前10項:")
for i in range(10):
print(f"F({i}) = {fibonacci(i)}")
輸出結果:
斐波那契數列前10項:
F(0) = 0
F(1) = 1
F(2) = 1
F(3) = 2
F(4) = 3
F(5) = 5
F(6) = 8
F(7) = 13
F(8) = 21
F(9) = 34
目錄遍歷¶
import os
def list_files(directory, level=0):
"""遞歸遍歷目錄結構"""
try:
items = os.listdir(directory)
for item in items:
item_path = os.path.join(directory, item)
indent = " " * level # 根據層級添加縮進
if os.path.isdir(item_path):
print(f"{indent}{item}/")
list_files(item_path, level + 1) # 遞歸調用
else:
print(f"{indent}{item}")
except PermissionError:
print(f"{indent}[權限不足]")
# 遍歷當前目錄(注意:這會顯示很多文件,這裏只是示例)
# list_files(".", 0)
print("目錄遍歷函數已定義,可以調用 list_files('目錄路徑') 來使用")
輸出結果:
目錄遍歷函數已定義,可以調用 list_files('目錄路徑') 來使用
遞歸與迭代的對比¶
# 遞歸版本的階乘
def factorial_recursive(n):
if n <= 1:
return 1
return n * factorial_recursive(n - 1)
# 迭代版本的階乘
def factorial_iterative(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
# 性能比較
import time
n = 10
# 測試遞歸版本
start_time = time.time()
recursive_result = factorial_recursive(n)
recursive_time = time.time() - start_time
# 測試迭代版本
start_time = time.time()
iterative_result = factorial_iterative(n)
iterative_time = time.time() - start_time
print(f"遞歸結果: {recursive_result}, 耗時: {recursive_time:.6f}秒")
print(f"迭代結果: {iterative_result}, 耗時: {iterative_time:.6f}秒")
print(f"結果相同: {recursive_result == iterative_result}")
輸出結果:
遞歸結果: 3628800, 耗時: 0.000003秒
迭代結果: 3628800, 耗時: 0.000002秒
結果相同: True
5.6 lambda表達式¶
lambda表達式的概念¶
lambda表達式是創建匿名函數的簡潔方式,適用於簡單的函數定義。
基本語法¶
# lambda參數: 表達式
# 簡單的lambda表達式
square = lambda x: x ** 2
print(f"5的平方: {square(5)}")
# 多個參數的lambda表達式
add = lambda x, y: x + y
print(f"3 + 7 = {add(3, 7)}")
# 帶默認參數的lambda表達式
greet = lambda name, greeting="Hello": f"{greeting}, {name}!"
print(greet("Alice"))
print(greet("Bob", "Hi"))
輸出結果:
5的平方: 25
3 + 7 = 10
Hello, Alice!
Hi, Bob!
lambda與內置函數的結合使用¶
與map()函數結合¶
# 使用lambda和map()處理列表
numbers = [1, 2, 3, 4, 5]
# 計算每個數的平方
squares = list(map(lambda x: x ** 2, numbers))
print(f"原數字: {numbers}")
print(f"平方值: {squares}")
# 將攝氏度轉換爲華氏度
celsius = [0, 20, 30, 40]
fahrenheit = list(map(lambda c: c * 9/5 + 32, celsius))
print(f"攝氏度: {celsius}")
print(f"華氏度: {fahrenheit}")
輸出結果:
原數字: [1, 2, 3, 4, 5]
平方值: [1, 4, 9, 16, 25]
攝氏度: [0, 20, 30, 40]
華氏度: [32.0, 68.0, 86.0, 104.0]
與filter()函數結合¶
# 使用lambda和filter()過濾列表
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 過濾出偶數
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"原數字: {numbers}")
print(f"偶數: {even_numbers}")
# 過濾出大於5的數
greater_than_five = list(filter(lambda x: x > 5, numbers))
print(f"大於5的數: {greater_than_five}")
# 過濾字符串列表
words = ["apple", "banana", "cherry", "date", "elderberry"]
long_words = list(filter(lambda word: len(word) > 5, words))
print(f"原單詞: {words}")
print(f"長度大於5的單詞: {long_words}")
輸出結果:
原數字: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
偶數: [2, 4, 6, 8, 10]
大於5的數: [6, 7, 8, 9, 10]
原單詞: ['apple', 'banana', 'cherry', 'date', 'elderberry']
長度大於5的單詞: ['banana', 'cherry', 'elderberry']
與sorted()函數結合¶
# 使用lambda進行自定義排序
students = [
{"name": "Alice", "age": 20, "grade": 85},
{"name": "Bob", "age": 19, "grade": 92},
{"name": "Charlie", "age": 21, "grade": 78},
{"name": "Diana", "age": 20, "grade": 96}
]
# 按年齡排序
sorted_by_age = sorted(students, key=lambda student: student["age"])
print("按年齡排序:")
for student in sorted_by_age:
print(f" {student['name']}: {student['age']}歲")
# 按成績排序(降序)
sorted_by_grade = sorted(students, key=lambda student: student["grade"], reverse=True)
print("\n按成績排序(降序):")
for student in sorted_by_grade:
print(f" {student['name']}: {student['grade']}分")
輸出結果:
按年齡排序:
Bob: 19歲
Alice: 20歲
Diana: 20歲
Charlie: 21歲
按成績排序(降序):
Diana: 96分
Bob: 92分
Alice: 85分
Charlie: 78分
5.7 內置函數詳解¶
數學函數¶
# 數學相關的內置函數
numbers = [-5, -2.7, 0, 3.14, 8]
print("數學函數示例:")
print(f"abs(-5) = {abs(-5)}") # 絕對值
print(f"round(3.14159, 2) = {round(3.14159, 2)}") # 四捨五入
print(f"pow(2, 3) = {pow(2, 3)}") # 冪運算
print(f"min(numbers) = {min(numbers)}") # 最小值
print(f"max(numbers) = {max(numbers)}") # 最大值
print(f"sum(numbers) = {sum(numbers)}") # 求和
輸出結果:
數學函數示例:
abs(-5) = 5
round(3.14159, 2) = 3.14
pow(2, 3) = 8
min(numbers) = -5
max(numbers) = 8
sum(numbers) = 3.44
類型轉換函數¶
# 類型轉換函數
print("類型轉換示例:")
print(f"int('123') = {int('123')}")
print(f"float('3.14') = {float('3.14')}")
print(f"str(42) = '{str(42)}'")
print(f"bool(1) = {bool(1)}")
print(f"bool(0) = {bool(0)}")
# 集合類型轉換
original_list = [1, 2, 2, 3, 3, 3]
print(f"\n原列表: {original_list}")
print(f"轉爲集合: {set(original_list)}")
print(f"轉爲元組: {tuple(original_list)}")
print(f"轉爲字典: {dict(enumerate(original_list))}")
輸出結果:
類型轉換示例:
int('123') = 123
float('3.14') = 3.14
str(42) = '42'
bool(1) = True
bool(0) = False
原列表: [1, 2, 2, 3, 3, 3]
轉爲集合: {1, 2, 3}
轉爲元組: (1, 2, 2, 3, 3, 3)
轉爲字典: {0: 1, 1: 2, 2: 2, 3: 3, 4: 3, 5: 3}
序列操作函數¶
# 序列操作函數
fruits = ["apple", "banana", "cherry"]
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("序列操作示例:")
print(f"len(fruits) = {len(fruits)}")
print(f"sorted(numbers) = {sorted(numbers)}")
print(f"list(reversed(fruits)) = {list(reversed(fruits))}")
# enumerate()函數
print("\nenumerate()示例:")
for index, fruit in enumerate(fruits):
print(f" 索引{index}: {fruit}")
# zip()函數
colors = ["red", "yellow", "dark red"]
print("\nzip()示例:")
for fruit, color in zip(fruits, colors):
print(f" {fruit}是{color}色的")
輸出結果:
序列操作示例:
len(fruits) = 3
sorted(numbers) = [1, 1, 2, 3, 4, 5, 6, 9]
list(reversed(fruits)) = ['cherry', 'banana', 'apple']
enumerate()示例:
索引0: apple
索引1: banana
索引2: cherry
zip()示例:
apple是red色的
banana是yellow色的
cherry是dark red色的
高階函數詳解¶
reduce()函數¶
from functools import reduce
# reduce()函數示例
numbers = [1, 2, 3, 4, 5]
# 計算所有數字的乘積
product = reduce(lambda x, y: x * y, numbers)
print(f"數字列表: {numbers}")
print(f"所有數字的乘積: {product}")
# 找出最大值
max_value = reduce(lambda x, y: x if x > y else y, numbers)
print(f"最大值: {max_value}")
# 字符串連接
words = ["Hello", "World", "Python", "Programming"]
sentence = reduce(lambda x, y: x + " " + y, words)
print(f"單詞列表: {words}")
print(f"連接後的句子: {sentence}")
輸出結果:
數字列表: [1, 2, 3, 4, 5]
所有數字的乘積: 120
最大值: 5
單詞列表: ['Hello', 'World', 'Python', 'Programming']
連接後的句子: Hello World Python Programming
其他常用內置函數¶
# any()和all()函數
list1 = [True, True, True]
list2 = [True, False, True]
list3 = [False, False, False]
print("any()和all()函數示例:")
print(f"all({list1}) = {all(list1)}")
print(f"all({list2}) = {all(list2)}")
print(f"any({list2}) = {any(list2)}")
print(f"any({list3}) = {any(list3)}")
# isinstance()函數
print("\nisinstance()函數示例:")
value = 42
print(f"isinstance({value}, int) = {isinstance(value, int)}")
print(f"isinstance({value}, str) = {isinstance(value, str)}")
print(f"isinstance({value}, (int, float)) = {isinstance(value, (int, float))}")
# hasattr()函數
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, I'm {self.name}"
person = Person("Alice")
print("\nhasattr()函數示例:")
print(f"hasattr(person, 'name') = {hasattr(person, 'name')}")
print(f"hasattr(person, 'greet') = {hasattr(person, 'greet')}")
print(f"hasattr(person, 'age') = {hasattr(person, 'age')}")
輸出結果:
any()和all()函數示例:
all([True, True, True]) = True
all([True, False, True]) = False
any([True, False, True]) = True
any([False, False, False]) = False
isinstance()函數示例:
isinstance(42, int) = True
isinstance(42, str) = False
isinstance(42, (int, float)) = True
hasattr()函數示例:
hasattr(person, 'name') = True
hasattr(person, 'greet') = True
hasattr(person, 'age') = False
本章小結¶
本章詳細介紹了Python函數的各個方面:
- 函數定義與調用:學習瞭如何定義函數、編寫文檔字符串以及調用函數
- 參數傳遞:掌握了位置參數、關鍵字參數、默認參數的使用方法
- 可變參數:瞭解了args和*kwargs的用法,以及參數解包操作
- 返回值與作用域:學習了函數返回值的處理和變量作用域的概念
- 遞歸函數:掌握了遞歸的基本原理和經典應用
- lambda表達式:學會了使用lambda創建匿名函數
- 內置函數:詳細瞭解了Python提供的各種內置函數
函數是Python編程的核心概念,掌握好函數的使用對於編寫高質量的Python代碼至關重要。在實際編程中,要根據具體需求選擇合適的函數定義方式和參數傳遞方法,同時注意作用域的管理和遞歸的合理使用。