堅持用 清晰易懂的圖解 + 代碼語言,讓每個知識點變得簡單!
🚀呆頭個人主頁詳情
🌱 呆頭個人Gitee代碼倉庫
📌 呆頭詳細專欄系列
座右銘: “不患無位,患所以立。”
Python函數:從入門到精通,一文掌握函數編程精髓
- 前言
- 目錄
- 一、函數是什么
- 1.函數的基本語法
- 二、函數參數
- 1.位置參數
- 2.默認參數
- 3.關鍵字參數
- 4.可變參數
- 5.關鍵字可變參數
- 6.參數類型對比
- 三、函數返回值
- 1.單值返回
- 2.多值返回
- 3.無返回值函數
- 四、變量作用域
- 1.局部變量與全局變量
- 2.使用global關鍵字
- 3.變量作用域可視化
- 五、函數的高級特性
- 1.函數作為對象
- 2.嵌套函數
- 3.閉包
- 4.裝飾器
- 5.裝飾器工作原理
- 六、函數的應用場景
- 1.數據處理
- 2.代碼復用
- 3.回調函數
- 4.函數執行過程
- 七、函數參數傳遞機制
- 1.可變對象與不可變對象
- 2.參數傳遞機制可視化
- 八、函數設計最佳實踐
- 1.單一職責原則
- 2.函數命名
- 3.參數設計
- 4.文檔字符串
- 九、函數調試技巧
- 1.打印調試
- 2.使用斷言
- 3.使用日志
- 十、函數性能優化
- 1.避免重復計算
- 2.使用生成器
- 3.使用適當的數據結構
- 十一、函數式編程
- 1.lambda函數
- 2.map、filter和reduce
- 3.列表推導式
- 十二、函數的常見錯誤與陷阱
- 1.默認參數陷阱
- 2.變量作用域問題
- 3.遞歸深度限制
- 總結
- 參考鏈接
- 關鍵詞標簽
前言
🔥 “靈根檢測發現:閣下竟是萬中無一的編程奇才!”
這是一個用修仙世界觀解構Python學習的硬核專欄:
- 練氣期:變量/循環/函數(基礎心法)
- 筑基期:面向對象/異常處理(護體罡氣)
- 金丹期:爬蟲/數據分析(神通初成)
- 元嬰期:Django/機器學習(開辟紫府)
?? 你將獲得:
? 每章配套「渡劫雷法」(實戰項目)
? 避免走火入魔的「心魔警示」(避坑指南)
? 飛升大能的「神識傳承」(大佬代碼賞析)“三千大道,Py為尊。本座在此布道,助你斬盡BUG,證道代碼金仙!”
(正文開始👇)
目錄
一、函數是什么
函數本質上是一段可重用的代碼塊,它接收輸入(參數),執行特定任務,并返回結果。在Python中,函數不僅僅是組織代碼的方式,更是實現抽象和封裝的重要工具。
1.函數的基本語法
Python函數定義的基本語法如下:
def 函數名(參數1, 參數2, ...):"""文檔字符串:描述函數功能"""# 函數體return 返回值 # 可選
一個簡單的函數示例:
def greet(name):"""向指定的人打招呼"""return f"你好,{name}!"# 調用函數
message = greet("小明")
print(message) # 輸出:你好,小明!
在這個例子中,greet
是函數名,name
是參數,函數體很簡單,只有一行代碼,返回一個格式化的字符串。
二、函數參數
Python函數的參數系統非常靈活,提供了多種參數類型來滿足不同的需求。
1.位置參數
最基本的參數類型,調用時按照定義的順序傳遞參數值。
def power(base, exponent):"""計算base的exponent次方"""return base ** exponentresult = power(2, 3) # 2的3次方 = 8
print(result) # 輸出:8
2.默認參數
為參數提供默認值,調用時可以省略這些參數。
def power(base, exponent=2):"""默認計算平方"""return base ** exponentprint(power(3)) # 3的2次方 = 9
print(power(3, 4)) # 3的4次方 = 81
3.關鍵字參數
通過參數名指定參數值,可以不按順序傳參。
def describe_pet(animal_type, pet_name):"""描述寵物信息"""return f"我有一只{animal_type},它叫{pet_name}。"# 使用關鍵字參數
print(describe_pet(pet_name="球球", animal_type="貓")) # 輸出:我有一只貓,它叫球球。
4.可變參數
使用*args
接收任意數量的位置參數,以元組形式存儲。
def sum_all(*numbers):"""計算所有參數的和"""total = 0for num in numbers:total += numreturn totalprint(sum_all(1, 2, 3, 4, 5)) # 輸出:15
5.關鍵字可變參數
使用**kwargs
接收任意數量的關鍵字參數,以字典形式存儲。
def build_profile(**user_info):"""創建用戶資料字典"""return user_infoprofile = build_profile(name="小明", age=25, city="北京", hobby="編程")
print(profile) # 輸出:{'name': '小明', 'age': 25, 'city': '北京', 'hobby': '編程'}
6.參數類型對比
下面的表格總結了Python函數的不同參數類型:
參數類型 | 語法 | 示例 | 特點 |
---|---|---|---|
位置參數 | def func(param1, param2) | func(1, 2) | 必須按順序提供所有參數 |
默認參數 | def func(param1, param2=value) | func(1) 或 func(1, 3) | 可選參數,有默認值 |
關鍵字參數 | def func(param1, param2) | func(param2=2, param1=1) | 通過參數名指定,順序靈活 |
可變位置參數 | def func(*args) | func(1, 2, 3, 4) | 接收任意數量的位置參數 |
關鍵字可變參數 | def func(**kwargs) | func(a=1, b=2, c=3) | 接收任意數量的關鍵字參數 |
三、函數返回值
Python函數可以返回單個值、多個值,或者不返回任何值(默認返回None
)。
1.單值返回
最常見的返回形式,函數執行完畢后返回一個值。
def square(number):"""返回數字的平方"""return number ** 2result = square(4)
print(result) # 輸出:16
2.多值返回
Python函數可以同時返回多個值,實際上是返回一個元組。
def get_dimensions(length, width):"""計算矩形的周長和面積"""perimeter = 2 * (length + width)area = length * widthreturn perimeter, areap, a = get_dimensions(5, 3)
print(f"周長:{p},面積:{a}") # 輸出:周長:16,面積:15
3.無返回值函數
如果函數沒有return
語句,或者return
后面沒有表達式,函數將返回None
。
def greet(name):"""打印問候語,無返回值"""print(f"你好,{name}!")result = greet("小紅") # 打印:你好,小紅!
print(result) # 輸出:None
四、變量作用域
理解變量作用域對于編寫正確的函數至關重要。Python中有局部作用域和全局作用域。
1.局部變量與全局變量
# 全局變量
message = "你好,世界!"def greet():# 局部變量name = "小明"print(f"{message} {name}")greet() # 輸出:你好,世界! 小明
# print(name) # 錯誤:name不在全局作用域中
2.使用global關鍵字
使用global
關鍵字可以在函數內部修改全局變量。
counter = 0def increment():global countercounter += 1return counterprint(increment()) # 輸出:1
print(increment()) # 輸出:2
print(counter) # 輸出:2
3.變量作用域可視化
圖1:變量作用域圖 - 展示了Python中全局變量和局部變量的作用范圍及相互影響
五、函數的高級特性
Python函數具有許多高級特性,使其成為一種強大的編程工具。
1.函數作為對象
在Python中,函數是一等公民,可以像其他對象一樣被賦值、傳遞和返回。
def greet(name):return f"你好,{name}!"# 函數賦值給變量
say_hello = greet
print(say_hello("小明")) # 輸出:你好,小明!# 函數作為參數傳遞
def apply_function(func, value):return func(value)result = apply_function(greet, "小紅")
print(result) # 輸出:你好,小紅!
2.嵌套函數
在函數內部定義另一個函數,內部函數可以訪問外部函數的變量。
def outer_function(x):"""外部函數"""def inner_function(y):"""內部函數"""return x + yreturn inner_functionadd_five = outer_function(5)
print(add_five(3)) # 輸出:8
print(add_five(7)) # 輸出:12
3.閉包
閉包是一個函數,它記住了創建它時的環境。
def make_multiplier(factor):"""創建一個乘法器"""def multiplier(number):return number * factorreturn multiplierdouble = make_multiplier(2)
triple = make_multiplier(3)print(double(5)) # 輸出:10
print(triple(5)) # 輸出:15
4.裝飾器
裝飾器是一種特殊的函數,它接受一個函數作為參數并返回一個新函數。
def timer_decorator(func):"""計時裝飾器"""import timedef wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()print(f"函數 {func.__name__} 執行時間:{end_time - start_time:.4f} 秒")return resultreturn wrapper@timer_decorator
def slow_function():"""一個耗時的函數"""import timetime.sleep(1)return "函數執行完畢"print(slow_function())
# 輸出:
# 函數 slow_function 執行時間:1.0010 秒
# 函數執行完畢
5.裝飾器工作原理
圖2:裝飾器工作原理時序圖 - 展示了Python裝飾器的執行流程和工作機制
六、函數的應用場景
Python函數在不同場景下有著廣泛的應用。下面我們來看幾個典型的應用場景。
1.數據處理
函數可以用于處理和轉換數據。
def process_data(data_list):"""處理數據列表,返回處理后的結果"""results = []for item in data_list:# 數據處理邏輯processed = item * 2 + 1results.append(processed)return resultsdata = [1, 2, 3, 4, 5]
processed_data = process_data(data)
print(processed_data) # 輸出:[3, 5, 7, 9, 11]
2.代碼復用
函數是實現代碼復用的基本單位。
def calculate_area(shape, *dimensions):"""計算不同形狀的面積"""if shape == "rectangle":return dimensions[0] * dimensions[1]elif shape == "circle":import mathreturn math.pi * dimensions[0] ** 2elif shape == "triangle":return 0.5 * dimensions[0] * dimensions[1]else:return None# 計算不同形狀的面積
rectangle_area = calculate_area("rectangle", 5, 4)
circle_area = calculate_area("circle", 3)
triangle_area = calculate_area("triangle", 6, 8)print(f"矩形面積:{rectangle_area}") # 輸出:矩形面積:20
print(f"圓形面積:{circle_area:.2f}") # 輸出:圓形面積:28.27
print(f"三角形面積:{triangle_area}") # 輸出:三角形面積:24.0
3.回調函數
函數可以作為參數傳遞給其他函數,實現回調機制。
def apply_operation(numbers, operation):"""對數字列表應用指定操作"""return [operation(num) for num in numbers]def square(x):return x ** 2def cube(x):return x ** 3numbers = [1, 2, 3, 4, 5]
squared = apply_operation(numbers, square)
cubed = apply_operation(numbers, cube)print(f"平方結果:{squared}") # 輸出:平方結果:[1, 4, 9, 16, 25]
print(f"立方結果:{cubed}") # 輸出:立方結果:[1, 8, 27, 64, 125]
4.函數執行過程
讓我們通過一個圖表來可視化函數的執行過程:
圖4:函數執行旅程圖 - 展示了Python函數從調用到返回的完整執行過程
七、函數參數傳遞機制
Python的參數傳遞機制是"傳對象引用",這意味著函數接收的是對象的引用,而不是對象的副本。
1.可變對象與不可變對象
理解可變對象和不可變對象對于理解函數參數傳遞至關重要。
# 不可變對象作為參數
def modify_string(s):s = s + " World"print(f"函數內部:{s}")text = "Hello"
modify_string(text)
print(f"函數外部:{text}")
# 輸出:
# 函數內部:Hello World
# 函數外部:Hello# 可變對象作為參數
def modify_list(lst):lst.append(4)print(f"函數內部:{lst}")numbers = [1, 2, 3]
modify_list(numbers)
print(f"函數外部:{numbers}")
# 輸出:
# 函數內部:[1, 2, 3, 4]
# 函數外部:[1, 2, 3, 4]
2.參數傳遞機制可視化
圖5:參數類型分布餅圖 - 展示了Python中不同類型參數的使用比例
八、函數設計最佳實踐
編寫高質量的函數需要遵循一些最佳實踐。
1.單一職責原則
每個函數應該只做一件事,并且做好。
# 不好的設計:函數做了太多事情
def process_and_save_data(data, filename):# 處理數據processed_data = []for item in data:processed_data.append(item * 2)# 保存數據with open(filename, 'w') as f:for item in processed_data:f.write(f"{item}\n")return processed_data# 好的設計:職責分離
def process_data(data):"""只負責處理數據"""return [item * 2 for item in data]def save_data(data, filename):"""只負責保存數據"""with open(filename, 'w') as f:for item in data:f.write(f"{item}\n")# 使用
data = [1, 2, 3, 4, 5]
processed = process_data(data)
save_data(processed, "output.txt")
2.函數命名
函數名應該清晰地表達函數的功能,通常使用動詞或動詞短語。
# 不好的命名
def x(a, b):return a + b# 好的命名
def add_numbers(a, b):return a + b
3.參數設計
函數參數應該設計得直觀且易于使用。
# 不好的參數設計
def create_user(p1, p2, p3, p4, p5):# ...pass# 好的參數設計
def create_user(username, email, password, first_name=None, last_name=None):# ...pass
4.文檔字符串
為函數添加文檔字符串,說明函數的功能、參數和返回值。
def calculate_discount(price, discount_rate):"""計算折扣后的價格。參數:price (float): 原始價格discount_rate (float): 折扣率,0到1之間的小數返回:float: 折扣后的價格示例:>>> calculate_discount(100, 0.2)80.0"""if not 0 <= discount_rate <= 1:raise ValueError("折扣率必須在0到1之間")return price * (1 - discount_rate)
“函數應該做一件事,只做一件事,并且把這件事做好。” —— Robert C. Martin(Uncle Bob)
九、函數調試技巧
調試函數是編程過程中不可避免的一部分。以下是一些有用的調試技巧。
1.打印調試
最簡單的調試方法是使用print
語句輸出變量值和執行流程。
def complex_calculation(a, b, c):print(f"輸入參數: a={a}, b={b}, c={c}")intermediate = a * bprint(f"中間結果: {intermediate}")result = intermediate / cprint(f"最終結果: {result}")return resulttry:complex_calculation(5, 2, 0)
except Exception as e:print(f"發生錯誤: {e}")
2.使用斷言
斷言可以幫助驗證函數的前置條件和后置條件。
def divide(a, b):"""安全除法,確保除數不為零"""assert b != 0, "除數不能為零"return a / btry:result = divide(10, 0)
except AssertionError as e:print(f"斷言錯誤: {e}")
3.使用日志
對于復雜的應用程序,使用日志比打印更靈活。
import logging# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')def process_item(item):"""處理單個項目"""logging.debug(f"開始處理項目: {item}")try:result = item * 2logging.info(f"項目 {item} 處理成功,結果: {result}")return resultexcept Exception as e:logging.error(f"處理項目 {item} 時出錯: {e}")raise# 使用函數
items = [1, 2, "3", 4]
for item in items:try:process_item(item)except Exception as e:logging.warning(f"跳過項目,繼續處理下一個")
十、函數性能優化
優化函數性能可以從多個方面入手。
1.避免重復計算
使用緩存可以避免重復計算,提高性能。
# 使用字典作為緩存
fibonacci_cache = {}def fibonacci(n):"""計算斐波那契數列的第n項"""# 檢查緩存if n in fibonacci_cache:return fibonacci_cache[n]# 計算值if n <= 1:value = nelse:value = fibonacci(n-1) + fibonacci(n-2)# 緩存結果fibonacci_cache[n] = valuereturn value# 使用functools.lru_cache裝飾器
from functools import lru_cache@lru_cache(maxsize=128)
def fibonacci_optimized(n):"""使用lru_cache優化的斐波那契函數"""if n <= 1:return nreturn fibonacci_optimized(n-1) + fibonacci_optimized(n-2)# 比較性能
import timedef measure_time(func, *args):start = time.time()result = func(*args)end = time.time()print(f"{func.__name__} 執行時間: {end - start:.6f} 秒")return resultn = 35
measure_time(fibonacci, n)
measure_time(fibonacci_optimized, n)
2.使用生成器
對于處理大量數據的函數,使用生成器可以減少內存使用。
# 使用列表(一次性加載所有數據到內存)
def get_squares_list(n):"""返回0到n-1的平方列表"""return [i**2 for i in range(n)]# 使用生成器(按需生成數據)
def get_squares_generator(n):"""返回0到n-1的平方生成器"""for i in range(n):yield i**2# 使用列表
squares_list = get_squares_list(1000000) # 立即分配大量內存# 使用生成器
squares_gen = get_squares_generator(1000000) # 不分配額外內存
for i, square in enumerate(squares_gen):if i < 10: # 只處理前10個print(square)else:break
3.使用適當的數據結構
選擇合適的數據結構可以顯著提高函數性能。
import time
import random# 準備測試數據
data = list(range(10000))
random.shuffle(data)
search_items = random.sample(data, 1000)# 使用列表查找
def find_in_list(items, search_items):found = 0for search_item in search_items:if search_item in items: # 列表查找是O(n)found += 1return found# 使用集合查找
def find_in_set(items_set, search_items):found = 0for search_item in search_items:if search_item in items_set: # 集合查找是O(1)found += 1return found# 比較性能
start = time.time()
found_list = find_in_list(data, search_items)
list_time = time.time() - startstart = time.time()
found_set = find_in_set(set(data), search_items)
set_time = time.time() - startprint(f"列表查找時間: {list_time:.6f} 秒")
print(f"集合查找時間: {set_time:.6f} 秒")
print(f"性能提升: {list_time/set_time:.2f}倍")
十一、函數式編程
Python支持函數式編程范式,提供了許多函數式編程的特性。
1.lambda函數
lambda函數是一種小型匿名函數,可以在需要函數對象的地方使用。
# 常規函數
def add(x, y):return x + y# 等價的lambda函數
add_lambda = lambda x, y: x + yprint(add(3, 5)) # 輸出:8
print(add_lambda(3, 5)) # 輸出:8
2.map、filter和reduce
這些高階函數是函數式編程的核心。
# map:對列表中的每個元素應用函數
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # 輸出:[1, 4, 9, 16, 25]# filter:過濾列表中的元素
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # 輸出:[2, 4]# reduce:將列表中的元素累積為單個值
from functools import reduce
product = reduce(lambda x, y: x * y, numbers)
print(product) # 輸出:120 (1*2*3*4*5)
3.列表推導式
列表推導式是一種簡潔的創建列表的方式,可以替代map和filter的組合。
numbers = [1, 2, 3, 4, 5]# 使用map和filter
squared_evens = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, numbers)))# 使用列表推導式
squared_evens_comp = [x**2 for x in numbers if x % 2 == 0]print(squared_evens) # 輸出:[4, 16]
print(squared_evens_comp) # 輸出:[4, 16]
十二、函數的常見錯誤與陷阱
編寫函數時,有一些常見的錯誤和陷阱需要避免。
1.默認參數陷阱
使用可變對象作為默認參數值可能導致意外行為。
# 錯誤的做法
def add_item(item, items=[]):items.append(item)return itemsprint(add_item("apple")) # 輸出:['apple']
print(add_item("banana")) # 輸出:['apple', 'banana'] - 可能不是預期結果# 正確的做法
def add_item_fixed(item, items=None):if items is None:items = []items.append(item)return itemsprint(add_item_fixed("apple")) # 輸出:['apple']
print(add_item_fixed("banana")) # 輸出:['banana']
2.變量作用域問題
在函數內部引用外部變量時,需要注意作用域規則。
x = 10def update_x():x = 20 # 創建了一個局部變量x,而不是修改全局變量print(f"函數內部x = {x}")update_x()
print(f"函數外部x = {x}")
# 輸出:
# 函數內部x = 20
# 函數外部x = 10def update_x_correctly():global xx = 20 # 修改全局變量print(f"函數內部x = {x}")update_x_correctly()
print(f"函數外部x = {x}")
# 輸出:
# 函數內部x = 20
# 函數外部x = 20
3.遞歸深度限制
Python對遞歸深度有限制,超過限制會導致棧溢出。
import sys
print(f"最大遞歸深度: {sys.getrecursionlimit()}")def factorial(n):"""計算階乘"""if n <= 1:return 1return n * factorial(n - 1)try:result = factorial(1000) # 可能導致棧溢出
except RecursionError as e:print(f"遞歸錯誤: {e}")# 使用尾遞歸優化(Python不會自動優化尾遞歸)
def factorial_tail(n, acc=1):if n <= 1:return accreturn factorial_tail(n - 1, n * acc)# 或者使用循環代替遞歸
def factorial_loop(n):result = 1for i in range(1, n + 1):result *= ireturn result
總結
隨著Python的不斷發展,函數的特性也在不斷豐富。類型提示、異步編程、函數式編程特性的引入,都為我們提供了更多的工具來編寫高質量的代碼。保持學習的熱情,跟上這些新特性的步伐,才能在Python編程的道路上走得更遠。
希望這篇文章能夠幫助你更好地理解和使用Python函數,讓我們一起用函數的力量,構建更加優雅、高效的Python程序!
📢 如果你也喜歡這種"不呆頭"的技術風格:
👁? 【關注】 看一個非典型程序員如何用野路子解決正經問題
👍 【點贊】 給"不寫八股文"的技術分享一點鼓勵
🔖 【收藏】 把這些"奇怪但有用"的代碼技巧打包帶走
💬 【評論】 來聊聊——你遇到過最"呆頭"的 Bug 是啥?
🗳? 【投票】 您的投票是支持我前行的動力
技術沒有標準答案,讓我們一起用最有趣的方式,寫出最靠譜的代碼! 🎮💻
參考鏈接
- Python官方文檔 - 函數定義
- PEP 484 – Type Hints
- Python函數式編程指南
- Python裝飾器的工作原理
- Python異步編程入門
關鍵詞標簽
#Python函數 #函數編程 #裝飾器 #閉包 #異步函數 #函數式編程 #類型提示