Python中通过__name__属性获取当前函数名

Ryan
Ryan
发布时间:2026-11-01| 10人浏览过
原创

详细讲解在Python中如何使用__name__属性获取函数名称,剖析全局__name__与函数对象属性的区别,探讨函数内部获取自身名称的方法及装饰器下的名称保留机制。

在开发日志记录、调试输出、性能分析以及错误追踪功能时,经常需要输出当前被调用函数的具体名称。如果在每处打印语句中硬编码函数名称字符串,后续一旦对函数进行重命名或重构,就必须逐行同步修改,极易遗漏。Python 作为一门具备高度自省(Introspection)能力的语言,其函数属于一等对象,内部默认绑定了专用于记录标识信息的 __name__ 属性。本文将具体讲解如何利用该属性获取函数名称,分析函数内外部调用的差异,指出初学者最常遇到的作用域陷阱,并给出装饰器场景下的规范做法。

一、核心认知:函数对象的 __name__ 与模块级 __name__

在 Python 中,每个使用 def 声明的函数本质上都是一个 function 类的实例对象。在函数定义生效时,解释器会自动将其标识符字符串写入该对象的 __name__ 特殊属性中。因此,只要拥有该函数对象的引用,即可直接读取该属性。

很多初学者在尝试获取当前函数名时,容易直接在函数体内调用 __name__,结果打印出的却是 "__main__" 或所属模块的文件名。这是因为没有区分清楚“全局变量作用域中的模块名”与“函数对象的专属属性”。

Python中通过__name__属性获取当前函数名
图 1:模块级全局 __name__ 变量与函数对象 __name__ 属性的作用域隔离关系

二、基本用法:获取已知函数对象的名称

当我们在代码中拥有函数的引用(无论是指向函数的变量、作为参数传入的高阶函数,还是字典中注册的回调函数),都可以通过点号表达式直接读取 __name__ 属性。

def process_payment(order_id, amount):
    """处理用户订单支付"""
    return f"订单 {order_id} 支付完成,金额: {amount}"

# 直接获取函数名
func_name = process_payment.__name__
print(f"当前操作的函数名是: {func_name}")

# 输出示例:
# 当前操作的函数名是: process_payment

在分发调度器或路由机制中,这一属性尤为实用。例如根据传入的函数名动态构建日志前缀或 API 映射表:

def register_route(handler):
    endpoint = f"/api/v1/{handler.__name__}"
    print(f"已成功注册接口路由: {endpoint}")

def fetch_user_profile():
    pass

register_route(fetch_user_profile)
# 示例输出: 已成功注册接口路由: /api/v1/fetch_user_profile

三、在函数内部获取“当前函数自身”的名称

如果需要在函数内部获取正在执行的自身名称,通常有两种主要路径:通过显式标识符访问,或通过运行时调用栈进行动态自省。

1. 方式一:在函数内部直接通过函数名访问(推荐日常使用)

最简单且开销最小的方式是在函数体内直接引用该函数的变量名并访问 __name__:

def sync_database_records():
    current_name = sync_database_records.__name__
    print(f"[{current_name}] 任务开始执行...")

sync_database_records()
# 示例输出: [sync_database_records] 任务开始执行...

这种写法执行速度极快,没有额外的调用栈反射开销。唯一的限制是如果之后重命名函数名,函数体内的这一行也需要同步改动。

2. 方式二:通过 inspect 或 sys 模块动态获取当前函数名

如果正在编写基础库、通用日志工具类,或者希望函数重命名时完全不需要修改函数体内部逻辑,可以通过 Python 的运行时帧(Frame)对象动态解析当前正在执行的函数名:

import sys
import inspect

def audit_log_action():
    # 使用 sys._getframe 获取当前调用帧中的代码对象名称
    func_name_sys = sys._getframe().f_code.co_name
    
    # 或者使用标准库 inspect 获取
    func_name_inspect = inspect.currentframe().f_code.co_name
    
    print(f"动态获取的当前执行函数: {func_name_sys} (验证: {func_name_inspect})")

audit_log_action()
# 示例输出: 动态获取的当前执行函数: audit_log_action (验证: audit_log_action)
原理解析:sys._getframe().f_code.co_name 返回的是当前底层代码块(Code Object)的名称。虽然它不是直接访问函数对象的 __name__ 属性,但在绝大多数标准函数执行场景下,其值与该函数的 __name__ 完全一致,并且不受函数在当前命名空间中是否被改名或重绑定的影响。
Python中通过__name__属性获取当前函数名
图 2:获取当前函数名的常用实现路径与选择步骤

四、进阶场景:装饰器对 __name__ 的遮蔽与恢复

在实际工程中,最容易让 __name__ 产生非预期结果的场景是自定义装饰器。当编写一个用于打印耗时或权限校验的装饰器时,通常会在内部定义一个 wrapper 闭包函数并返回它。此时,如果不作额外处理,原函数的标识将被 wrapper 取代。

def bad_timer(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@bad_timer
def generate_report():
    pass

print(generate_report.__name__)
# 示例输出: wrapper  <-- 原函数名丢失,变成了包装器的名字!

这一现象会导致依赖函数名的日志系统、文档生成工具(Sphinx)或单元测试框架出现混淆。为了解决这一问题,Python 标准库提供了 functools.wraps 装饰器。它的核心工作就是将被装饰函数的 __name__、__doc__ 以及模块信息完整拷贝到内部的 wrapper 对象上:

from functools import wraps

def good_timer(func):
    @wraps(func)  # 关键:自动保留原函数的 __name__ 等元数据
    def wrapper(*args, **kwargs):
        print(f"正在准备调用函数: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@good_timer
def generate_report():
    pass

print(f"当前真实函数名: {generate_report.__name__}")
# 示例输出: 当前真实函数名: generate_report
Python中通过__name__属性获取当前函数名
图 3:装饰器场景下 __name__ 的遮蔽与 @functools.wraps 复制保留机制

五、常见边界情况与注意事项

在面对不同类型的可调用对象时,__name__ 的表现各有不同,需要留意以下边界情况:

  • 匿名函数 (Lambda):使用 lambda 表达式创建的匿名函数,其 __name__ 的固定返回值均为字符串 "<lambda>"。如果系统需要唯一且具可读性的名称,建议使用显式 def 定义。
  • 偏函数 (functools.partial):通过 partial 构造的偏函数对象本身属于 partial 类实例,并不具备 __name__ 属性。如需获取底层原函数名,需要访问其 func 属性,即 p.func.__name__。
  • 可调用类实例:如果一个类实现了 __call__ 方法,其实例对象也是可调用的,但该实例对象没有 __name__ 属性。获取其实际名称应查询其类名:type(instance).__name__。
  • 类内方法:类中定义的普通方法、类方法(@classmethod)和静态方法(@staticmethod)均包含 __name__。对于需要完整类限定名的情况,建议在 Python 3.3+ 中使用 __qualname__ 属性(例如 ClassName.method_name)。
Python中通过__name__属性获取当前函数名
图 4:Python各类可调用对象与自省机制下的函数名获取速查总结

六、总结

掌握 __name__ 属性的使用,是编写可维护、高质量 Python 代码的重要一环:

  1. 常规对象访问:直接使用 func.__name__,清晰、高效且符合直觉。
  2. 注意作用域区分:不要在函数体内直接写无修饰的 __name__,那代表模块全局变量;函数内部访问自身应使用 函数名.__name__。
  3. 解耦与通用化:在日志工具或抽象基类中,可以使用 sys._getframe().f_code.co_name 获取当前执行上下文的函数名,降低与具体名称的耦合。
  4. 装饰器规范:任何包装函数的装饰器都应加上 @functools.wraps(func),确保原函数的 __name__ 能够无损传递。
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。