为何Python函数中局部导入模块时会触发UnboundLocalError?
哦,这个问题确实有点隐蔽,我来给你拆解一下背后的原因——这其实是Python作用域规则的一个“坑”,很多人第一次碰到都会困惑。
核心原因:Python的作用域扫描机制
Python在编译函数体的时候,会从头到尾扫描所有代码,寻找对变量名的赋值操作(注意:import语句本质上也是一种赋值——它把模块对象绑定到对应的名字上)。一旦发现某个名字在函数内部有赋值行为,Python就会把这个名字标记为局部变量,不管这个赋值是在条件分支里、循环里,还是函数的后半段。
回到你的代码:
import foo.bar def qux(): if(foo.bar.fred()): import foo.baz # 这里的import会让Python把foo标记为局部变量 print(foo.baz.waldo()) qux()
虽然import foo.baz是在if块内部,但Python编译qux函数时已经检测到了这个语句,它会认定foo是qux函数的局部变量。当代码执行到if(foo.bar.fred())这一行时,Python会直接去局部作用域找foo,但此时foo还没有被赋值(因为if块还没执行,import foo.baz根本没跑),所以就抛出了UnboundLocalError——哪怕你在全局作用域已经导入了foo.bar也没用,Python已经认定foo是局部的了。
解决方案
你有几种办法可以修复这个问题:
把
import foo.baz移到函数外部
既然你在函数里需要用到foo.baz,直接在全局作用域导入就好了,这样函数内部的foo会直接引用全局的模块对象:import foo.bar import foo.baz def qux(): if(foo.bar.fred()): print(foo.baz.waldo()) qux()用
global声明foo是全局变量
在函数开头告诉Python,foo是全局作用域的名字,不要把它当作局部变量:import foo.bar def qux(): global foo # 声明foo是全局变量 if(foo.bar.fred()): import foo.baz print(foo.baz.waldo()) qux()换一种导入方式,避免修改
foo名字
用from foo import baz代替import foo.baz,这样就不会在局部作用域绑定foo这个名字了:import foo.bar def qux(): if(foo.bar.fred()): from foo import baz print(baz.waldo()) qux()
内容的提问来源于stack exchange,提问作者cowlinator

