如何从独立文件中定义的基类动态创建全局变量?
如何从独立文件中定义的基类动态创建全局变量?
问题根源
你遇到的NameError其实是因为**globals()**指向的是定义函数/类的模块的全局命名空间,而不是调用它的模块的!比如在mylib.py里的Parent类中调用globals(),操作的是mylib模块的全局变量,而不是test.py的,所以test.py自然找不到temperature这个变量。
解决方案1:获取调用者的全局命名空间(用inspect模块)
我们可以通过Python的inspect模块获取调用栈,拿到调用者的上下文环境,从而操作调用者的全局变量。修改你的mylib.py如下:
# mylib.py import inspect class Parent: def __init__(self, var_name): self.__var_name__ = var_name def create_global_variable(self, var_value): # 获取调用者的栈帧,index=1代表上层调用者(跳过当前方法本身) caller_frame = inspect.stack()[1] # 操作调用者的全局命名空间 caller_frame.frame.f_globals[self.__var_name__] = var_value # 记得释放栈帧,避免内存泄漏 caller_frame.frame.f_locals.clear() del caller_frame def clear_global_variables(self): caller_frame = inspect.stack()[1] caller_frame.frame.f_globals.clear() caller_frame.frame.f_locals.clear() del caller_frame
然后在test.py里运行,就能正常打印100了:
# test.py from mylib import Parent class Child(Parent): pass child = Child('temperature') child.create_global_variable(100) print(temperature) # 输出100 child.clear_global_variables()
解决方案2:让调用者主动传入自己的全局命名空间(更可靠)
如果不想依赖inspect模块(毕竟栈操作在某些特殊场景下可能有问题),可以直接让调用者把自己的globals()传进来,这样逻辑更清晰:
修改mylib.py:
# mylib.py import inspect class Parent: def __init__(self, var_name): self.__var_name__ = var_name def create_global_variable(self, var_value, caller_globals=None): # 默认用调用者的globals,也可以让用户手动传 target_globals = caller_globals or inspect.stack()[1].frame.f_globals target_globals[self.__var_name__] = var_value def clear_global_variables(self, caller_globals=None): target_globals = caller_globals or inspect.stack()[1].frame.f_globals target_globals.clear()
调用的时候可以显式传参:
# test.py from mylib import Parent class Child(Parent): pass child = Child('temperature') child.create_global_variable(100, caller_globals=globals()) print(temperature) # 输出100
针对你补充的job()函数场景
你的job()函数要在f-string里使用,并且要动态创建全局变量,同样可以用上面的思路。这里推荐让job()接受一个globals参数,这样更符合你的约束:
def job(input, predicate, buffer_name, caller_globals=None): import inspect target_globals = caller_globals or inspect.stack()[1].frame.f_globals target_globals[buffer_name] = predicate(input) return input # 在f-string里调用时传入当前的globals() exp = fr""" Squaring {job(2,lambda x: x*x, 'square', caller_globals=globals())} equals to {square} """ print(exp)
运行后会输出:
Squaring 2 equals to 4
这样就满足了你“所有缓冲变量必须定义在f-string”的要求,因为job()的调用和变量square的引用都在同一个f-string里。
注意事项
- 动态创建全局变量其实是一种比较“魔法”的操作,容易导致代码难以维护,除非必要,建议优先考虑用字典、类属性等方式来管理动态变量。
- 如果用
inspect模块,记得及时释放栈帧,避免内存泄漏。
备注:内容来源于stack exchange,提问作者D G
相关产品推荐
相关产品推荐

