深度拆解 Python 裝飾器參數傳遞:從裝飾器生效到參數轉交的每一步

在 Python 裝飾器的學習中,“被裝飾函數的參數如何傳遞到裝飾器內層函數”是一個高頻疑問點。很多開發者能寫出裝飾器的基本結構,卻對參數傳遞的底層邏輯一知半解。本文將以一段具體代碼為例,把參數傳遞過程拆成“裝飾器生效→調用觸發→參數捕獲→參數轉交”4個階段,逐行解析 Python 解釋器的執行邏輯,帶你看清每一個細節。

一、先看完整代碼:我們的分析樣本

為了讓分析更聚焦,先給出完整的裝飾器與被裝飾函數代碼,后續所有拆解都圍繞這段代碼展開:

# 1. 定義裝飾器函數 zhuangshiqi
def zhuangshiqi(func):print("zhuangshiqi里")  # 裝飾器生效時執行# 2. 定義裝飾器內層函數 hanshu(參數傳遞的核心載體)def hanshu(*arg, **kwarg):print("hanshu里")    # 被裝飾函數調用時執行print(f"捕獲的位置參數:{arg}")  # 打印捕獲的位置參數print(f"捕獲的關鍵字參數:{kwarg}")  # 打印捕獲的關鍵字參數return func(*arg, **kwarg)  # 將參數轉交原函數return hanshu  # 裝飾器返回內層函數# 3. 用 @ 語法糖為函數 ts 綁定裝飾器
@zhuangshiqi
def ts(a, b, /, c, d, *e, f, args, **kwargs):"""被裝飾的目標函數,包含多種參數類型"""return "函數執行結束"# 4. 調用被裝飾后的 ts 函數
ts(1, 2, 3, 4, 1, f=2, args=3, kwargs=4)

這段代碼包含了 Python 中常見的參數類型(強制位置參數 /、可變位置參數 *e、關鍵字-only 參數 f、可變關鍵字參數 **kwargs),以及標準的裝飾器嵌套結構,是分析參數傳遞的理想樣本。

二、階段1:裝飾器生效(@zhuangshiqi的“魔法”)——ts的指向被偷偷替換

很多人誤以為 @zhuangshiqi 只是一個“標記”,告訴 Python“這個函數需要被裝飾”。但實際上,@ 是 Python 的語法糖,它會在解釋器讀取到函數定義時立即執行,完成“目標函數指向替換”的核心操作。這一步是參數能傳遞到內層函數的前提,必須先徹底理解。

1.2 解釋器的執行順序(逐行拆解)

當 Python 解釋器運行這段代碼時,會按以下順序處理:

步驟1:讀取裝飾器定義,但不執行

解釋器先讀取 def zhuangshiqi(func):...,在內存中創建一個名為 zhuangshiqi 的函數對象,此時僅完成“定義”,不會執行函數內部的代碼(比如 print("zhuangshiqi里") 不會觸發)。

步驟2:讀取 @zhuangshiqi,記錄裝飾器綁定關系

解釋器讀取到 @zhuangshiqi 時,會記住:“接下來定義的函數 ts,需要用 zhuangshiqi 這個裝飾器處理”。這里的 @ 語法,本質是后續操作的“觸發器”。

步驟3:定義原函數 ts,此時 ts 指向原函數

解釋器繼續讀取 def ts(a, b, /, c, d, *e, f, args, **kwargs):...,在內存中創建一個“原 ts 函數對象”,此時變量 ts 指向這個原函數對象(可以理解為:ts 是原函數的“名字標簽”)。

步驟4:觸發裝飾器邏輯,完成 ts 指向替換(關鍵!)

這是最核心的一步:@zhuangshiqi 等價于執行 ts = zhuangshiqi(ts)。解釋器會按以下子步驟執行:

  • 子步驟4.1:將“原 ts 函數對象”作為參數 func,傳入裝飾器 zhuangshiqi
  • 子步驟4.2:執行 zhuangshiqi(func) 內部代碼:
    • 首先執行 print("zhuangshiqi里"),所以運行代碼時,你會先看到這句話(不是調用 ts 時才看到);
    • 然后定義內層函數 hanshu(*arg, **kwarg):在內存中創建 hanshu 函數對象,此時 hanshu 僅完成定義,不執行內部代碼;
    • 最后執行 return hanshu:將 zhuangshiqi 的返回值(即 hanshu 函數對象)賦值給變量 ts
  • 子步驟4.3:最終結果:變量 ts 不再指向“原 ts 函數對象”,而是指向“內層函數 hanshu 對象”。

1.3 關鍵結論:ts 成了 hanshu 的“別名”

經過階段1后,當你后續寫 ts(...) 時,Python 解釋器會認為你要調用的是 hanshu(...)——這是參數能傳遞到 hanshu 的根本原因。如果把函數比作“人”,ts 這個“名字”原本屬于原函數,現在被裝飾器“偷換”給了 hanshu

三、階段2:調用 ts(...)——實際是調用 hanshu(...),參數傳遞的起點

當你執行 ts(1, 2, 3, 4, 1, f=2, args=3, kwargs=4) 時,因為階段1已經完成了“名字替換”,Python 會直接調用 hanshu(1, 2, 3, 4, 1, f=2, args=3, kwargs=4)。這一步是參數傳遞的“起點”,所有傳給 ts 的參數,都會原封不動地傳給 hanshu

2.1 先明確:傳給 ts 的參數類型

在分析傳遞過程前,先拆解你傳入的參數 1, 2, 3, 4, 1, f=2, args=3, kwargs=4,按 Python 的參數分類規則,它們可分為兩類:

  • 位置參數1, 2, 3, 4, 1——沒有 key= 前綴,靠“傳入順序”匹配函數形參;
  • 關鍵字參數f=2, args=3, kwargs=4——有 key= 前綴,靠“key 與形參名匹配”來綁定。

這兩類參數會被 Python 打包成一個“參數集合”,完整傳遞給 hanshu

四、階段3:hanshu(*arg, **kwarg)——“全捕獲”所有參數,打包成元組和字典

hanshu 的形參定義 (*arg, **kwarg) 是 Python 中“捕獲任意參數”的標準寫法,它能把傳給 hanshu 的所有參數,按“位置/關鍵字”分類打包,這是參數傳遞的核心環節。

4.1 *arg:捕獲所有位置參數,打包成元組

  • 作用*arg 中的 * 是“位置參數捕獲符”,它會把傳給 hanshu 的所有“位置參數”(即 1, 2, 3, 4, 1)按傳入順序收集起來,打包成一個元組(tuple),并賦值給變量 arg
  • 執行結果arg = (1, 2, 3, 4, 1)
  • 為什么用元組:元組是“不可變序列”,一旦創建就不能修改。這樣設計能避免參數在 hanshu 中被意外篡改,確保后續轉交給原函數的參數是“原始值”。

4.2 **kwarg:捕獲所有關鍵字參數,打包成字典

  • 作用**kwarg 中的 ** 是“關鍵字參數捕獲符”,它會把傳給 hanshu 的所有“關鍵字參數”(即 f=2, args=3, kwargs=4)按“key:value”的形式收集起來,打包成一個字典(dict),并賦值給變量 kwarg
  • 執行結果kwarg = {'f': 2, 'args': 3, 'kwargs': 4}
  • 為什么用字典:關鍵字參數的核心是“通過名字匹配形參”,字典的 key 正好能對應參數名,value 對應參數值,結構清晰且便于后續“解包”。

4.3 驗證捕獲結果:打印參數

此時 hanshu 內部的 print(f"捕獲的位置參數:{arg}")print(f"捕獲的關鍵字參數:{kwarg}") 會執行,輸出結果如下:

捕獲的位置參數:(1, 2, 3, 4, 1)
捕獲的關鍵字參數:{'f': 2, 'args': 3, 'kwargs': 4}

這證明 hanshu 已經完整捕獲了所有傳給 ts 的參數,沒有遺漏。

五、階段4:return func(*arg, **kwarg)——參數“解包”,轉交原 ts 函數

hanshu 捕獲參數后,不是自己使用,而是要“轉交”給“原 ts 函數”(也就是階段1中傳入 zhuangshiqifunc,因為 func 指向原 ts)。這里的 *arg**kwarg 又承擔了新的角色——“參數解包器”。

5.1 *arg:把元組“解包”成原始位置參數

arg(1, 2, 3, 4, 1),在 func(*arg) 中,* 會把元組“拆成單個的位置參數”,即:
*arg → 拆成 1, 2, 3, 4, 1(和你最初傳給 ts 的位置參數完全一樣)。

5.2 **kwarg:把字典“解包”成原始關鍵字參數

kwarg{'f': 2, 'args': 3, 'kwargs': 4},在 func(**kwarg) 中,** 會把字典“拆成 key=value 形式的關鍵字參數”,即:
**kwarg → 拆成 f=2, args=3, kwargs=4(和你最初傳給 ts 的關鍵字參數完全一樣)。

5.3 最終轉交:原 ts 拿到完整參數,正常執行

func(*arg, **kwarg) 等價于直接調用原 ts 函數:
原ts(1, 2, 3, 4, 1, f=2, args=3, kwargs=4)——這正是你最初想調用的參數組合!

此時原 ts 會按自己的形參定義(a, b, /, c, d, *e, f, args, **kwargs)匹配參數:

  • a=1, b=2/ 左側的強制位置參數,只能通過位置匹配;
  • c=3, d=4/ 右側的普通位置參數,通過位置匹配;
  • *e=(1):可變位置參數,收集剩下的位置參數 1
  • f=2:關鍵字-only 參數(*e 之后的參數默認是關鍵字-only),通過 f=2 匹配;
  • args=3:普通關鍵字參數,通過 args=3 匹配;
  • **kwargs={'kwargs':4}:可變關鍵字參數,收集剩下的關鍵字參數 kwargs=4

ts 接收參數后,執行 return "函數執行結束",這個返回值會被 hanshu 接收,再返回給 ts() 的調用者——整個參數傳遞鏈路完全閉環。

六、總結:參數傳遞的“完整鏈路圖”與形象類比

6.1 鏈路圖:用一句話串起所有階段

你調用 ts(參數) → 因 @zhuangshiqi 替換,實際調用 hanshu(參數) → hanshu 用 *arg/**kwarg 把參數分別打包成元組/字典 → hanshu 用 */* 把元組/字典拆成原始參數 → 轉交原 ts 執行 → 原 ts 返回結果,hanshu 再把結果返回給你

6.2 形象類比:用“快遞運輸”理解參數傳遞

如果把參數比作“快遞”,整個過程就像一次完整的快遞運輸:

  1. 你(調用者)把“快遞”(參數)交給“快遞點 ts”;
  2. 但“快遞點 ts”的招牌被“總公司 zhuangshiqi”換成了“中轉站 hanshu”(裝飾器生效階段);
  3. “中轉站 hanshu”把“快遞”按“類型”(位置/關鍵字)分類打包:位置參數裝成“紙箱”(元組),關鍵字參數裝成“快遞袋”(字典)(參數捕獲階段);
  4. “中轉站 hanshu”把打包好的“快遞”拆回原樣,交給“最終收件人原 ts”(參數轉交階段);
  5. “原 ts”處理完快遞(執行函數),把“回執”(返回值)通過“中轉站 hanshu”交回給你。

七、關鍵注意點:避免參數傳遞中的常見問題

  1. *arg, **kwarg 的順序不能亂hanshu 的形參必須是 (*arg, **kwarg),不能寫成 (**kwarg, *arg)——Python 規定,位置參數捕獲符 *arg 必須在關鍵字參數捕獲符 **kwarg 之前,否則會報語法錯誤;
  2. 裝飾器生效時機@zhuangshiqi 會在函數定義時立即執行,不是調用時執行——如果裝飾器內有耗時操作(如讀取文件),會在程序啟動時就觸發,可能影響啟動速度;
  3. 原函數元信息保留:本文的示例中,hanshu 沒有保留原 ts 的元信息(如 __name____doc__),調用 print(ts.__name__) 會輸出 hanshu,而非 ts。實際開發中,建議用 functools.wraps 保留元信息,修改后的 hanshu 如下:
import functools
def zhuangshiqi(func):@functools.wraps(func)  # 保留原函數元信息def hanshu(*arg, **kwarg):# 原有邏輯return func(*arg, **kwarg)return hanshu

通過以上4個階段的拆解,相信你已經徹底理解了裝飾器中參數傳遞的每一個細節。Python 裝飾器的參數傳遞,本質是“名字替換+參數打包+參數解包”的組合,只要抓住這三個核心操作,無論遇到多復雜的裝飾器結構,都能理清參數的流向。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/96686.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/96686.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/96686.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Vue2 ?】Vue2 入門之旅 · 進階篇(七):Vue Router 原理解析

在前幾篇文章中,我們介紹了 Vue 的性能優化機制、組件緩存等內容。本篇將深入解析 Vue Router 的原理,了解 Vue 如何管理路由并進行導航。 目錄 Vue Router 的基本概念路由模式:hash 和 history路由匹配原理導航守衛Vue Router 的路由過渡動…

Linux磁盤級文件/文件系統理解

Linux磁盤級文件/文件系統理解 1. 磁盤的物理結構 磁盤的核心是一個利用磁性介質和機械運動進行數據讀寫的、非易失性的存儲設備。 1.1 盤片 盤片是傳統機械硬盤中最核心的部件,它是數據存儲的物理載體。盤片是一個堅硬的、表面極度光滑的圓形碟片,被安裝…

【星海出品】rabbitMQ - 叁 應用篇

rabbitMQ 的基礎知識這里就不闡述了,可以參看我早年寫的文章 -> rabbitMQ 入門 https://blog.csdn.net/weixin_41997073/article/details/118724779 Celery 官網:http://www.celeryproject.org/ Celery 官方文檔英文版:http://docs.celeryproject.org/en/latest/index.h…

C# 每個chartArea顯示最小值、平均值、最大值

private void AddStatisticsAnnotations(ChartArea chartArea, int channelIndex) {RemoveExistingAnnotations(channelIndex);// 獲取ChartArea的相對坐標(百分比)float chartAreaX chartArea.Position.X; // X坐標(百分比)floa…

打破“不可能三角”:WALL-OSS開源,具身智能迎來“安卓時刻”?

目錄 引言:當“大腦”學會思考,機器人才能走出實驗室 一、具身智能的“不可能三角”:機器人“大腦”的核心困境 二、WALL-OSS的四把重錘:如何系統性地破解難題? 2.1 第一錘:更聰明的“大腦”架構 —— …

SigNoz分布式追蹤新體驗:cpolar實現遠程微服務監控

前言 SigNoz是一款開源的應用性能監控工具,專為微服務架構設計,集成了指標、追蹤和日志分析功能。它能夠全面監控分布式系統的性能,幫助開發團隊快速定位問題根源。SigNoz支持OpenTelemetry協議,可以無縫集成各種編程語言和框架&…

python編程原子化多智能體綜合編程應用(下)

上述代碼實現了基于Mesa框架的診斷智能體類,包含以下核心功能: 模塊化設計:通過類屬性分離數據與行為,支持不同專科智能體的擴展 狀態管理:實現idle/processing/error等狀態轉換,支持任務調度 診斷推理:集成機器學習模型,支持癥狀提取與多分類診斷 錯誤處理:包含模型加…

QT M/V架構開發實戰:QSqlQueryModel/ QSqlTableModel/ QSqlRelationalTableModel介紹

目錄[TOC](目錄)前言一、初步介紹二、QSqlQueryModel1.基礎定位2.特點3.核心接口4.典型用法5.優缺點三、QSqlTableModel1.基礎定位2.特點3.核心接口4.典型用法5.優缺點四、QSqlRelationalTableModel1.基礎定位2.特點3.核心接口4.典型用法 (示例:employees表有 dept_…

Terraform 從入門到實戰:歷史、原理、功能與阿里云/Azure 上手指南

前言:在云時代,企業的IT基礎設施早已從“幾臺服務器”演變為“橫跨多云的復雜網絡、計算、存儲集群”。但隨之而來的,是管理復雜度的爆炸式增長:開發環境和生產環境不一致、手動配置容易出錯、多云平臺操作方式各異、資源變更難以…

【計算機網絡 | 第10篇】信道復用技術

文章目錄信道復用技術:高效利用通信資源的智慧方案一、頻分復用(FDM):按頻率劃分的并行通道二、時分復用(TDM):按時間分割的輪流占用三、統計時分復用(STDM):…

安卓13_ROM修改定制化-----禁用 Android 導航按鍵的幾種操作

Android 設備的導航按鍵通常包括后退鍵(Back)、主頁鍵(Home)和最近鍵(Recents),這些按鍵位于屏幕底部或設備實體區域。禁用導航按鍵可以幫助在特定應用場景(如信息亭模式或兒童鎖模式)中限制用戶操作。安卓設備上禁用底部虛擬導航鍵(返回、主頁、多任務鍵)有多種方法…

通過S參數測量評估電感阻抗:第2部分

S21雙端口分流和雙端口串聯方法 T這是兩篇文章中的第二篇,專門討論使用網絡分析儀測量 S 參數進行電感阻抗評估主題。上一篇文章 [1] 描述了阻抗測量和計算S11使用單端口分流器、雙端口分流器和雙端口串聯方法的參數。本文專門介紹阻抗測量和計算S21使用雙端口分流…

[deepseek] C語言頭文件與匯編實現討論

我想詢問一種代碼實現方式,使用C語言,例如main.c包含了自己編寫的庫文件abc.h,我想問的是:一、abc.h中是否可以有實現函數的代碼;二、abc.h中的函數是否可以在另一個后綴為asm的匯編文件中實現?非常好&…

`.cursorrules` 與 `.cursorcontext`:Cursor AI 編程助手時代下的“雙軌配置”指南

.cursorrules 與 .cursorcontext:AI 編程助手時代下的“雙軌配置”指南關鍵詞:Cursor、AI 編程、上下文管理、開發規范、技術治理 適合讀者:前端 / 全棧工程師、技術負責人、AI 輔助編程實踐者1. 為什么又多了兩個“點”文件? 隨著…

XR 和 AI 在 Siggraph 2025 上主導圖形的未來,獲取gltf/glb格式

Meta 的 Boba 和 Tiramisu XR 耳機(來源:Meta) Siggraph 2025 今年重返不列顛哥倫比亞省溫哥華,慶祝《玩具總動員》誕生 30 周年和視頻游戲實時渲染 20 周年。雖然 Siggraph 需要時間來欣賞過去,但它更多的是展望未來…

在 Ubuntu 22.04 系統(CUDA 12.9)中,通過本地DEB 包安裝 cuDNN 9.13.0 的方法步驟

以下是在 Ubuntu 22.04 系統(CUDA 12.9)中,通過本地單個 DEB 包安裝 cuDNN 9.13.0 的完整步驟,核心包含 GPG 密鑰配置與包安裝驗證,確保每一步可執行。 一、安裝前核心檢查(必做) 確保系統已滿足基礎條件,避免安裝失敗: 驗證 CUDA 版本:打開終端執行命令,確認當前…

Element 中 upload 編輯回顯文件上傳信息技巧

文章目錄需求分析需求 upload 編輯狀態下回顯已上傳的文件信息 分析 添加fileList <el-uploadstyle"width: 100%"ref"uploadRef"class"upload-demo"action"/prod-api/jc/files/upload"multiple:limit"1":on-success&q…

php簡介(第一天打卡)

一.php簡介 1.什么是php&#xff1f; 1.1 Php 為什么叫這個名字&#xff1f; Personal home page 最開始用于個人主頁建站 后更名為 hypertext preprocessor 超文本預處理 1.2 php是屬于哪種語言&#xff1f; 后端語言 &#xff08;從開發角度分類&#xff09; 服務端語言…

Android 車聯網——車載儀表屏開發(二十六)

通常汽車啟動后需要快速顯示儀表,而車載娛樂系統所在的Android系統,啟動是比較耗時的,所以通常儀表系統會做在一個小型輕量化的系統內,從而達到快速啟動的效果,最終實現汽車一發動,就立刻能顯示出儀表必須顯示的各項內容。 一、儀表功能介紹 1、儀表的發展 機械儀表:通…

RL--RLHF--PPO--GRPO--DPO速通

參考視頻&#xff1a;1小時速通 - 從強化學習到RLHF - 簡介_嗶哩嗶哩_bilibili 強化學習RL RL的核心就是智能體Agent 與 環境Environment的交互。 狀態&#xff08;State&#xff0c;s&#xff09;&#xff1a;環境在某一時刻的描述&#xff0c;表示當前情境。動作&#xff0…