为何IPython交互式环境可动态创建类,模块调用却失败?
问题:动态创建类的环境差异现象
为何交互式环境中代码可行:
In [1]: def new_class(klass): ...: globals()[klass] = type(klass, (object,), {}) ...: In [2]: new_class('AA') In [3]: AA() Out[3]: <__main__.AA at 0x7f5629c5ddc0>
但从模块调用时却失效:
In [4]: from maker import * In [5]: new_class2?? Signature: new_class2(klass) Docstring: <no docstring> Source: def new_class2(klass): globals()[klass] = type(klass, (object,), {}) File: /my/py38/seq2struct/lib/maker.py Type: function In [6]: new_class2('AB') In [7]: AB() --------------------------------------------------------------------------- NameError Traceback (most recent call last) Input In [7], in <cell line: 1>() ----> 1 AB() NameError: name 'AB' is not defined
核心疑问
为何在IPython交互式环境中可通过上述方式动态创建类并实例化,而从模块调用相同逻辑的函数时,却无法访问动态创建的类?
原因解析
globals()的作用域本质:globals()返回的是函数定义所在模块的全局命名空间字典,和调用该函数的上下文无关。- 交互式环境中,
new_class定义在__main__模块(IPython主环境),globals()操作的就是当前环境的命名空间,所以创建的AA直接出现在当前环境,能直接访问。 - 当
new_class2定义在maker.py模块时,globals()指向的是maker模块的命名空间,调用new_class2('AB')后,AB类实际上被存在了maker模块里,而非当前调用的__main__环境,自然会报NameError。
- 交互式环境中,
验证与临时解决方法:
要访问这个动态创建的类,需要从maker模块中获取:# 方式1:直接导入 from maker import AB AB() # 方式2:引用模块属性 import maker maker.AB()让函数在调用者环境创建类的方法:
如果想让函数在调用它的命名空间里生成类,不能用globals(),可以通过inspect模块获取调用栈的上下文:import inspect def new_class2(klass): # 获取调用者的全局命名空间 caller_globals = inspect.currentframe().f_back.f_globals caller_globals[klass] = type(klass, (object,), {})此时调用
new_class2('AB'),AB会被直接添加到当前调用环境的命名空间,就能直接用AB()实例化。
内容的提问来源于stack exchange,提问作者sten
相关产品推荐
相关产品推荐

