Python高階精講之閉包與裝飾器徹底吃透(零基礎(chǔ)看懂+實(shí)戰(zhàn)源碼)
前言
很多Python開發(fā)者學(xué)了很久,依然看不懂裝飾器、不會(huì)手寫裝飾器、不懂閉包原理。
但閉包和裝飾器是Python進(jìn)階分水嶺:源碼、框架(Flask/Django)、緩存、日志、接口鑒權(quán)、性能統(tǒng)計(jì),底層全部依賴這套語(yǔ)法。
本文用最通俗的語(yǔ)言+分步拆解+完整實(shí)戰(zhàn)源碼,從零帶你吃透:函數(shù)進(jìn)階 → 閉包原理 → 裝飾器基礎(chǔ) → 有參裝飾器 → 多層裝飾器 → 企業(yè)級(jí)實(shí)戰(zhàn)場(chǎng)景,零基礎(chǔ)也能一次性學(xué)懂。
01 前置基礎(chǔ):函數(shù)是一等公民
Python中函數(shù)是一等對(duì)象(一等公民),擁有三個(gè)核心特性,這是閉包、裝飾器能實(shí)現(xiàn)的根本:
- 函數(shù)可以賦值給變量
- 函數(shù)可以作為參數(shù)、返回值
- 函數(shù)可以嵌套定義(內(nèi)部函數(shù))
基礎(chǔ)示例
# 1. 函數(shù)賦值給變量
def hello():
print("Hello Python")
f = hello
f()
# 2. 函數(shù)作為返回值
def outer():
def inner():
print("內(nèi)部函數(shù)執(zhí)行")
return inner
res = outer()
res()
核心總結(jié):正因?yàn)楹瘮?shù)可以嵌套、可以返回,才誕生了閉包。
02 閉包詳解:定義、條件、原理
2.1 閉包的必備三要素
同時(shí)滿足以下3點(diǎn),就是閉包(Closure):
- 函數(shù)嵌套(外層函數(shù)+內(nèi)層函數(shù))
- 內(nèi)層函數(shù)引用外層函數(shù)的局部變量
- 外層函數(shù)返回內(nèi)層函數(shù)
2.2 閉包實(shí)戰(zhàn)代碼
def outer(num):
# 外層局部變量
def inner():
# 內(nèi)層引用外層變量
print(f"傳入數(shù)值:{num}")
return inner
# 接收內(nèi)層函數(shù)
func = outer(100)
# 調(diào)用內(nèi)層函數(shù)
func()
2.3 閉包核心特性(重點(diǎn))
正常函數(shù)執(zhí)行結(jié)束后,局部變量會(huì)被銷毀釋放。
但閉包會(huì)保留外層函數(shù)的局部變量,不會(huì)被GC回收,實(shí)現(xiàn)了數(shù)據(jù)常駐、狀態(tài)保存。
2.4 閉包經(jīng)典場(chǎng)景:計(jì)數(shù)器
def count_factory():
cnt = 0
def counter():
nonlocal cnt
cnt += 1
print(f"當(dāng)前計(jì)數(shù):{cnt}")
return counter
c1 = count_factory()
c1() # 1
c1() # 2
c1() # 3
知識(shí)點(diǎn):nonlocal 關(guān)鍵字,用于內(nèi)層函數(shù)修改外層嵌套函數(shù)的局部變量。
03 裝飾器本質(zhì):基于閉包的語(yǔ)法糖
3.1 核心認(rèn)知
裝飾器 = 高階閉包 + 語(yǔ)法糖
裝飾器的核心作用:在不修改原函數(shù)代碼、不修改原函數(shù)調(diào)用方式的前提下,給函數(shù)新增功能。
這就是設(shè)計(jì)模式中的開閉原則:對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。
3.2 裝飾器解決的問(wèn)題
- 避免重復(fù)代碼(日志、計(jì)時(shí)、鑒權(quán)通用邏輯復(fù)用)
- 業(yè)務(wù)代碼與通用工具代碼解耦
- 統(tǒng)一項(xiàng)目規(guī)范,便于維護(hù)迭代
04 手寫無(wú)參裝飾器:日志統(tǒng)計(jì)實(shí)戰(zhàn)
需求:給任意函數(shù)添加執(zhí)行日志、執(zhí)行耗時(shí)統(tǒng)計(jì)
4.1 原生閉包寫法(理解底層)
import time
def timer_decorator(func):
# 通用裝飾器,接收任意參數(shù)
def wrapper(*args, **kwargs):
start = time.time()
# 執(zhí)行原函數(shù)
res = func(*args, **kwargs)
end = time.time()
print(f"函數(shù)【{func.__name__}】執(zhí)行耗時(shí):{end - start:.4f}s")
# 返回原函數(shù)結(jié)果
return res
return wrapper
# 測(cè)試函數(shù)
@timer_decorator
def calc_sum(n):
s = 0
for i in range(n):
s += i
return s
calc_sum(100000)
4.2 裝飾器執(zhí)行流程拆解
@timer_decorator裝飾器加載,等價(jià)于calc_sum = timer_decorator(calc_sum)- 調(diào)用
calc_sum()實(shí)際執(zhí)行的是內(nèi)部包裝函數(shù)wrapper() - wrapper執(zhí)行前置邏輯 → 執(zhí)行原函數(shù) → 后置邏輯 → 返回結(jié)果
4.3 解決函數(shù)信息覆蓋問(wèn)題
直接使用裝飾器會(huì)導(dǎo)致原函數(shù)的函數(shù)名、文檔注釋被wrapper覆蓋,需要 functools.wraps 修復(fù):
import time
from functools import wraps
def timer_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print(f"函數(shù)【{func.__name__}】執(zhí)行耗時(shí):{end - start:.4f}s")
return res
return wrapper
05 進(jìn)階:帶參數(shù)裝飾器 + 多層裝飾器
5.1 帶參數(shù)裝飾器
普通裝飾器只能接收函數(shù)參數(shù),帶參裝飾器可以自定義配置參數(shù),靈活性更高。
需求:自定義日志等級(jí),控制是否打印日志
from functools import wraps
def log_decorator(switch=True):
# 外層接收裝飾器參數(shù)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if switch:
print(f"【日志】函數(shù){func.__name__}開始執(zhí)行")
res = func(*args, **kwargs)
return res
return wrapper
return decorator
# 開啟日志
@log_decorator(switch=True)
def test_func():
print("業(yè)務(wù)邏輯執(zhí)行")
test_func()
5.2 多層裝飾器疊加執(zhí)行順序
加載順序:自下而上,執(zhí)行順序:自上而下
口訣:先裝飾的后執(zhí)行,后裝飾的先執(zhí)行
from functools import wraps
def deco1(func):
@wraps(func)
def wrapper():
print("deco1 前置")
func()
print("deco1 后置")
return wrapper
def deco2(func):
@wraps(func)
def wrapper():
print("deco2 前置")
func()
print("deco2 后置")
return wrapper
@deco1
@deco2
def hello():
print("核心業(yè)務(wù)執(zhí)行")
hello()
06 企業(yè)級(jí)實(shí)戰(zhàn)三大裝飾器(可直接復(fù)用)
6.1 接口鑒權(quán)裝飾器
from functools import wraps
def login_auth(func):
@wraps(func)
def wrapper(user_token, *args, **kwargs):
# 模擬鑒權(quán)邏輯
if not user_token:
return {"code": 401, "msg": "未登錄,權(quán)限不足"}
return func(user_token, *args, **kwargs)
return wrapper
# 模擬接口
@login_auth
def get_user_info(token):
return {"code": 200, "data": "用戶信息數(shù)據(jù)"}
print(get_user_info(""))
6.2 簡(jiǎn)易緩存裝飾器(避免重復(fù)計(jì)算)
from functools import wraps
def cache_decorator(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@cache_decorator
def fib(n):
if n <= 2:
return 1
return fib(n-1) + fib(n-2)
print(fib(30)) # 首次計(jì)算,后續(xù)直接讀取緩存
6.3 異常捕獲裝飾器(統(tǒng)一異常處理)
from functools import wraps
def exception_catch(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"【函數(shù)異?!縶func.__name__}執(zhí)行失?。簕str(e)}")
return None
return wrapper
@exception_catch
def divide(a, b):
return a / b
divide(10, 0)
07 面試高頻真題(必背)
Q1:什么是閉包?閉包的作用和缺點(diǎn)?
答:
閉包是嵌套函數(shù)中,內(nèi)層函數(shù)引用外層局部變量、外層返回內(nèi)層函數(shù)的代碼結(jié)構(gòu)。
作用:保存函數(shù)執(zhí)行狀態(tài)、實(shí)現(xiàn)數(shù)據(jù)常駐、簡(jiǎn)化代碼、實(shí)現(xiàn)裝飾器。
缺點(diǎn):外層變量常駐內(nèi)存無(wú)法釋放,長(zhǎng)期使用會(huì)造成內(nèi)存泄漏。
Q2:裝飾器的原理是什么?為什么不修改原代碼?
裝飾器基于閉包+高階函數(shù)實(shí)現(xiàn),通過(guò)包裝原函數(shù),在函數(shù)執(zhí)行前后擴(kuò)展邏輯,符合開閉原則,實(shí)現(xiàn)業(yè)務(wù)與通用邏輯解耦。
Q3:wraps裝飾器的作用?
修復(fù)裝飾器導(dǎo)致的原函數(shù)信息丟失問(wèn)題,保留原函數(shù)的 __name__、文檔注釋等屬性。
Q4:多層裝飾器的執(zhí)行順序?
加載自下而上,執(zhí)行自上而下,先裝飾的函數(shù)最后執(zhí)行后置邏輯。
到此這篇關(guān)于Python高階精講之閉包與裝飾器徹底吃透(零基礎(chǔ)看懂+實(shí)戰(zhàn)源碼)的文章就介紹到這了,更多相關(guān)Python閉包與裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文詳解Python中PO模式的設(shè)計(jì)與實(shí)現(xiàn)
在使用 Python 進(jìn)行編碼的時(shí)候,會(huì)使用自身自帶的編碼設(shè)計(jì)格式,比如說(shuō)最常見的單例模式等。本文將為大家介紹PageObject自動(dòng)化設(shè)計(jì)模式(PO模式)的設(shè)計(jì)與實(shí)現(xiàn),感興趣的可以了解一下2022-06-06
Python錯(cuò)誤和異??偨Y(jié)詳細(xì)
本文詳細(xì)且清晰地講解了Python中錯(cuò)誤和異常的概念及其處理方式,通過(guò)具體案例展示try...except、try...finally、with...等句式的具體用法,期望能幫助到對(duì)此感到迷惑的初學(xué)者2021-10-10
使用Python實(shí)現(xiàn)將HTML內(nèi)容格式插入Word文檔
在日常開發(fā)和業(yè)務(wù)流程中,我們經(jīng)常需要生成包含復(fù)雜格式和動(dòng)態(tài)內(nèi)容的Word文檔,本文將介紹如何使用Python實(shí)現(xiàn)將HTML內(nèi)容格式插入Word文檔,感興趣的小伙伴可以了解下2026-01-01
python MultipartEncoder傳輸zip文件實(shí)例
這篇文章主要介紹了python MultipartEncoder傳輸zip文件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
Python實(shí)現(xiàn)語(yǔ)音啟動(dòng)電腦應(yīng)用程序
這篇文章主要為大家詳細(xì)介紹了如何使用Python實(shí)現(xiàn)語(yǔ)音啟動(dòng)電腦應(yīng)用程序功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一學(xué)習(xí)一下2025-03-03
pandas 強(qiáng)制類型轉(zhuǎn)換 df.astype實(shí)例
這篇文章主要介紹了pandas 強(qiáng)制類型轉(zhuǎn)換 df.astype實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04

