Python 自省精簡示範:Frame 與 Code Object 入門; import inspect ; inspect.currentframe().f_code.co_name #動態取得function_name

加入好友
加入社群
Python 自省精簡示範:Frame 與 Code Object 入門; import inspect ; inspect.currentframe().f_code.co_name #動態取得function_name - 儲蓄保險王

在動態語言 Python 中,自省(introspection)讓你於執行期檢視程式自身:你可以知道「哪個函式正在跑」、「它最初定義在哪一行」、「當前這次呼叫的局部變數是什麼」。這種能力是除錯、產生文件、測試框架、追蹤呼叫、建 plugin/registry 系統的重要基礎。
然而初學者常把「函式」當成一個單一黑盒;實際上,一次函式呼叫至少牽涉三個層級:

Function 物件:承載可呼叫介面與 code
Code Object:編譯後的不可變結構(名稱、起始行、參數佈局、常量池等)。
Frame:本次呼叫的執行環境(當前行號、locals、上一層 f_back)。
本精簡版示例只保留最能打開視角的三個主題:
(a) 如何取得與觀察 frame / code。
(b) 行號差異:定義起始行 (co_firstlineno) vs 目前執行行 (f_lineno)。
(c) 快速列印 code object 常用屬性(變數、常量、自由變數)。
若你需要更進一步(簽名綁定、原始碼擷取、呼叫堆疊掃描、decorator metadata、閉包結構、生成器/協程判斷),請參考完整版範例;本檔案旨在降低認知負荷,先讓「frame / code / runtime」的 mental model 成形。

Section 1. Function 物件 (def 產出) vs Code Object (func.__code__) vs Frame:
透過 `inspect.currentframe()` 取得 frame,解析 `frame.f_code`, 比對 `co_name / co_filename / co_firstlineno` 與執行行號差異。

Section 2. 行號差異:
`frame.f_lineno` (目前執行位置) vs `co_firstlineno` (定義起始行)。

Section 3. 快速列印常見 code object 屬性:
封裝 `show_code_attrs`,便於觀察 參數數量、變數名稱、常量池、自由變數等核心結構。

"""my_demo_less.py
精簡版 Python introspection (自省) 示範
=================================================
此檔為完整版教學的精簡 / 入門版本僅保留最核心三個觀念

Section 1. Function 物件 vs Code Object vs Frame
           透過 `inspect.currentframe()` 取得 frame解析 `frame.f_code`
           比對 `co_name / co_filename / co_firstlineno` 與執行行號差異

Section 2. 行號差異`frame.f_lineno` (目前執行位置) vs `co_firstlineno` (定義起始行)。

Section 3. 快速列印常見 code object 屬性封裝 `show_code_attrs`便於觀察
           參數數量變數名稱常量池自由變數等核心結構

若需簽名解析原始碼抓取呼叫堆疊decorator metadata閉包深入
 generator / coroutine 判斷等進階主題請參考完整版 `my_demo.py`。)
"""

from __future__ import annotations
import inspect
import textwrap
from functools import wraps
import types

print("="*70)
print("[Section 1] Function / Code Object / Frame 基本關係")

def demo(x):
    """分層展示:frame -> frame.f_code -> code object 屬性

    教學路徑
            (a) 取得目前執行中的 frame (inspect.currentframe)
            (b) 檢視 frame 物件與其型別
            (c) 取出 frame.f_code (對應函式的 code object)
            (d) 檢視 code object 型別與核心屬性 (co_name / co_filename / co_firstlineno)
            (e) 比較 co_firstlineno (定義起始行)  frame.f_lineno (目前執行行)
            (f)  frame.f_code.co_name  globals 回推函式物件避免硬寫函式名

    重點function 物件 (demo) 與一次呼叫的 frame 是不同層級多次呼叫會產生多個 frame但共用同一個 code object
    """
    frame = inspect.currentframe()  # (a)
    print("(a) current frame obtained")
    print("    frame:        ", frame)             # (b)
    print("    type(frame):  ", type(frame))

    code = frame.f_code  # (c)
    # 說明
    # frame.f_code  (此函式物件).__code__ 指向同一個 code object
    # 若此函式目前名稱為 demo demo.__code__ is frame.f_code  True
    # 但直接硬寫 demo.__code__ 會綁定符號名稱」,將來重構改名需同步修改
    # 利用 frame.f_code 取得 code object 則是名稱無關”(name-agnostic),
    # 可再透過 frame.f_code.co_name 動態回推 globals 內對應函式而不依賴舊名稱
    # 若外部仍需要舊名稱相容可以建立 alias例如 new_name = demo
    print("(c) code = frame.f_code")
    print("    code object:  ", code)
    print("    type(code):   ", type(code))  # (d)

    resolved_func = frame.f_globals.get(code.co_name)  # (f)
    print("(f) resolved_func via globals[co_name]:", resolved_func)
    if resolved_func is not None:
        print("    resolved_func.__code__ is code:", resolved_func.__code__ is code)

    print("(d) code object core attributes")
    print("    co_name:       ", code.co_name)
    print("    co_filename:   ", code.co_filename)
    print("    co_firstlineno:", code.co_firstlineno)

    print("(e) runtime line compare")
    print("    frame.f_lineno (current line):", frame.f_lineno)
    print("    code.co_firstlineno (def line):", code.co_firstlineno)

    return x * 2

result_demo = demo(1)
print("demo(1) =>", result_demo)

print("="*70)
print("[Section 2] frame.f_lineno vs co_firstlineno 差異")

def line_progress():
    frame = inspect.currentframe()
    print("起始 co_firstlineno:", frame.f_code.co_firstlineno)
    # 下列列印行號會逐行變動
    print("目前執行行號 a:", frame.f_lineno)
    print("目前執行行號 b:", frame.f_lineno)  # 仍是本行解釋器求值完再遞增
    # 插入一個子函式呼叫觀察回來後行號
    _inner()
    print("回來後行號 c:", frame.f_lineno)

def _inner():
    pass

line_progress()

print("="*70)
print("[Section 3] 快速列印常見 code object 屬性")

def show_code_attrs(fn, attrs=None):
    """列印函式 code 物件的重要屬性 (精簡版)

    attrs: 若提供自訂屬性名稱列表否則使用預設精選集合
    """
    if attrs is None:
        attrs = [
            "co_name", "co_filename", "co_firstlineno",
            "co_argcount", "co_posonlyargcount", "co_kwonlyargcount",
            "co_varnames", "co_nlocals", "co_consts", "co_names",
            "co_freevars", "co_cellvars"
        ]
    code = fn.__code__
    print(f"<code attrs of {fn.__name__}>:")
    for a in attrs:
        val = getattr(code, a)
        # 過長內容截斷顯示 (例如 co_consts)
        text = repr(val)
        if len(text) > 120:
            text = text[:117] + '...'
        print(f"  {a:>15}: {text}")

# 示範列印 demo  code 屬性
show_code_attrs(demo)

輸出:

Python 自省精簡示範:Frame 與 Code Object 入門; import inspect ; inspect.currentframe().f_code.co_name #動態取得function_name - 儲蓄保險王
Python 自省精簡示範:Frame 與 Code Object 入門; import inspect ; inspect.currentframe().f_code.co_name #動態取得function_name - 儲蓄保險王

推薦hahow線上學習python: https://igrape.net/30afN

加入好友
加入社群
Python 自省精簡示範:Frame 與 Code Object 入門; import inspect ; inspect.currentframe().f_code.co_name #動態取得function_name - 儲蓄保險王

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡

You may also like...

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *