跨Python模块调用类属性触发NameError的问题及解决方案
解决跨模块调用中的NameError问题
问题重现
你现在遇到的是跨模块依赖里的名称作用域问题,先把你的代码和报错情况理清楚:
test1.py 代码:
class c1: pass class c2: def e(self): return c3.x
test2.py 代码:
from test1 import * class c3(c1): x = 1 def __init__(self,x): self.x = x class c4(c2): def __init__(self,y): self.y = y
执行以下操作时触发错误:
$ python >>> import test2 as t2 >>> import test1 as t1 >>> a = t2.c4(2) >>> b = t2.c3(4) >>> a.e()
报错提示:
NameError: name 'c3' is not defined
把所有类放同一个模块就正常,说明问题出在跨模块的名称引用上。
问题根源
其实很好理解:
test1里的c2.e()直接写了return c3.x,但c3是定义在test2里的,test1模块自己的作用域里根本不知道c3是什么。- 你在
test2里用from test1 import *只是把test1的内容拿到test2里用,并不会反过来把test2的类塞给test1,所以test1还是找不到c3。
已验证的解决方案
方案1:显式模块引用(你提到的验证方案)
修改test1.py,导入test2并使用模块限定名来访问c3:
import test2 class c1: pass class c2: def e(self): return test2.c3.x
运行注意点:必须先导入test2再导入test1,顺序不能搞反:
$ python >>> import test2 as t2 >>> import test1 as t1 >>> a = t2.c4(2) >>> b = t2.c3(4) >>> a.e() # 此时会返回4,正常运行
要是先导入test1,test2还没加载,test1里的import test2就会报错。
方案2:延迟导入(优化版,无需关心导入顺序)
如果不想被导入顺序限制,可以把c3的导入放到e()方法内部,只有调用方法时才去加载:
class c1: pass class c2: def e(self): from test2 import c3 return c3.x
这种方式不用在test1顶部导入test2,不管你先导入哪个模块,只要调用a.e()的时候,test2已经被加载(或者会自动触发加载),就能正常找到c3。
内容的提问来源于stack exchange,提问作者user9172285
相关产品推荐
相关产品推荐

