直接用 globals()["func_name"]() 报 NameError 是因函数未定义、被 del 或不在模块全局作用域(如在 if False 块或类中);getattr() 需指定对象(如模块或实例),不能直接用于函数名;带点路径名须用 importlib 逐级解析;高频调用应缓存函数对象并校验输入。

为什么直接用 globals()["func_name"]() 有时会报 NameError
因为 globals() 返回的是当前模块的全局符号表,但函数必须已定义、未被 del、且不在局部作用域(比如嵌套函数内部)——常见坑是:函数写在 if False: 块里,或条件导入后没执行到,导致名字根本没进 globals()。另外,如果函数在类里定义(即方法),它不属于模块全局,globals() 找不到。
getattr() 调用函数前必须先拿到对象
getattr() 本身不查模块,只从「某个对象」上取属性。想调用模块级函数,得传入模块对象,最常用的是 sys.modules[__name__] 或直接用 __import__(__name__);想调用类的方法,就得先实例化类或用类名作第一个参数。漏掉这步就会抛 AttributeError。
- 安全写法:
import sys func = getattr(sys.modules[__name__], "my_func", None) if callable(func): result = func() - 别写
getattr(my_func, ...)—— 这是在函数对象上取属性,不是找函数 - 类方法要这样:
getattr(MyClass, "method_name")(instance, ...)或用getattr(instance, "method_name")()
字符串含点号(如 "utils.helper.do_work")不能直接用 globals()
globals() 只支持一级名称,遇到带点的路径会当作一个完整键名去查,必然失败。正确做法是逐级导入:from utils.helper import do_work,或用 importlib 动态解析:
from importlib import import_module module_path, func_name = "utils.helper", "do_work" mod = import_module(module_path) func = getattr(mod, func_name) func()
- 别手动
split(".")然后反复__import__,容易错失子包的__init__.py导入逻辑 -
import_module自动处理相对导入和包结构,比裸__import__更可靠
性能和安全:别在热循环里反复查函数名
每次调用 globals() 或 getattr(...) 都是字典查找或属性访问,开销不大,但若在高频循环中做(比如每毫秒调一次),建议提前缓存函数对象:
立即学习“Python免费学习笔记(深入)”;
_cached_func = globals().get("heavy_calc")
# 后续直接用 _cached_func(),不重复查- 更推荐把函数名映射预先建好:
FUNC_MAP = {"calc": calc_func, "parse": parse_func} - 用户输入的函数名务必校验,避免执行任意代码:
if name not in FUNC_MAP: raise ValueError(f"Unknown function: {name}") -
eval(name + "()")或exec是严重安全隐患,绝对不要用
动态调用本身不难,难的是边界情况——函数是否真存在、在哪个作用域、有没有权限访问、字符串格式对不对。把这些检查揉进调用前,比事后抓 KeyError 或 AttributeError 省事得多。


















