You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中如何从内部函数对象获取其父函数的层级路径信息?

问题背景与需求

假设我们在my_module.py中定义如下函数:

from typing import Callable

def make_function(scalar: float) -> Callable[[float], float]:
    
    def increment(x: float) -> float:
        return x + scalar
    
    return increment

运行时获取内部函数对象时,其__repr__会显示包含父函数的信息:

>>> make_function(2)
<function my_module.make_function.<locals>.increment(x: float) -> float>

需求:有没有优雅的方式从该内部函数对象获取其父函数的相关信息?尤其需要动态生成类似my_module.make_function.increment格式的字符串,且支持任意嵌套层级(如my_module.outer_func_1.outer_func_2.inner_func)。

已尝试的方法:

  • inspect模块:inspect.getmodule(func).__name__仅返回模块路径,无法获取父函数链
  • 函数的__code__.co_*系列属性:未找到指向父函数的直接途径
  • 操作func.__globals__:需要遍历全局命名空间匹配,复杂度高且容易出错

理想方案:无需装饰或修改原函数,仅通过检查函数对象本身实现。


解决思路

方案1:利用__qualname__属性(Python 3.3+ 最优方案)

从Python 3.3开始,函数对象新增了__qualname__属性,它会直接返回函数的嵌套限定名,天然包含父函数层级信息,结合模块名即可生成完整路径:

import inspect

def get_full_nested_func_name(func):
    module_name = inspect.getmodule(func).__name__
    return f"{module_name}.{func.__qualname__}"

测试示例:

from my_module import make_function
inc_func = make_function(2)
print(inc_func.__qualname__)  # 输出: make_function.increment
print(get_full_nested_func_name(inc_func))  # 输出: my_module.make_function.increment

这是最优雅可靠的方案,__qualname__是Python标准定义的属性,专门用于表示嵌套函数/类的限定名称,完全适配任意嵌套层级的需求。

方案2:解析__repr__字符串(简单直接,依赖CPython实现)

如果需要兼容Python 3.3之前的版本,可以通过正则解析函数__repr__的输出内容,提取嵌套路径:

import re

def get_nested_func_name(func):
    repr_str = repr(func)
    match = re.search(r'<function (\S+)\(', repr_str)
    if not match:
        return func.__name__
    full_path = match.group(1)
    return full_path.replace('.<locals>', '')

测试示例:

from my_module import make_function
inc_func = make_function(2)
print(get_nested_func_name(inc_func))  # 输出: my_module.make_function.increment

注意:该方法依赖CPython的__repr__输出格式,其他Python实现(如PyPy)的输出可能不同,存在兼容性风险。

方案3:通过闭包回溯父函数(兼容旧版本,逻辑更严谨)

内部函数会通过闭包引用外部函数的变量,可借助inspect模块分析闭包信息,递归回溯父函数链:

import inspect

def get_parent_func(func):
    # 从闭包变量中查找父函数引用
    closure_vars = inspect.getclosurevars(func)
    for cell in closure_vars.nonlocals.values():
        if inspect.isfunction(cell):
            return cell
    # 尝试从自由变量对应的全局对象中查找
    for var_name in func.__code__.co_freevars:
        if var_name in func.__globals__:
            obj = func.__globals__[var_name]
            if inspect.isfunction(obj):
                return obj
    return None

def build_full_func_path(func):
    path_parts = [func.__name__]
    current_func = func
    while True:
        parent = get_parent_func(current_func)
        if not parent:
            break
        path_parts.insert(0, parent.__name__)
        current_func = parent
    # 拼接模块名
    module = inspect.getmodule(func)
    if module:
        path_parts.insert(0, module.__name__)
    return '.'.join(path_parts)

测试示例:

from my_module import make_function
inc_func = make_function(2)
print(build_full_func_path(inc_func))  # 输出: my_module.make_function.increment

说明:该方法兼容性更好,但如果内部函数没有引用父函数的变量(无闭包),则无法回溯到父函数,属于边界情况需额外处理。


内容的提问来源于stack exchange,提问作者Azmy Rajab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 16:45:17