推测出输出结果了吗?没错,会报错:NameError: name 'i' is not defined。切记:函数的命名空间在函数被调用时创建,函数执行完毕,命名就也被销毁。另外,LEGB搜索法则也不会让全局作用域去局部作用域寻找。
(2)情况2:if True: i = 1 print(i) # 可以正常输出i的值1,不会报错
if条件判断语句不会引入新的作用域,所以,语句“i=1”与“print(i)”属于同一作用域,既然同属于一个作用域,也不存在说if代码块运行完之后,作用域销毁,所以i一直存在,可以正常执行。
(3)情况3:for i in range(10): pass print(i) #输出结果是9,而不是NameError
for循环不会引入新的作用域,所以,循环结束后,继续执行print(i),可以正常输出i,原理上与情况3中的if相似。这一点Python就比较坑了,因此写代码时切忌for循环名字要与其他名字不重名才行。
(4)情况4list_1 = [i for i in range(5)] print(i)
情况3中说到过,for循环不会引入新的朱用于,那么为什么输出报错呢?真相只有一个:列表生成式会引入新的作用域,for循环是在Local作用域里面的。事实上,lambda、生成器表达式、列表解析式也是函数,都会引入新作用域。
(5)情况5:def import_sys(): import sys import_sys() print(sys.path) # 报错:NameError: name 'sys' is not defined
在函数内部进行模块导入时,导入的模块只在函数内部作用域生效。这个算非正常程序员的写法了,import语句在函数import_sys中将名字sys和对应模块绑定,那sys这个名字还是定义在局部作用域,跟上面的例子没有任务区别。要时刻切记Python的名字,对象,这个其他编程语言不一样。
(6)情况6:只引用上层作用域中的值时:
def test(): print(i)# 可正常输出0 i = 0 test()
在局部作用域中可以引用全局作用域中的命名空间。
注:可不要认为i=0这行必须卸载def test()前面,事实上只需要在test()函数调用前写i=0即可,因为函数的命名空间是在函数被调用时创建的。
继续上面的例子,若是对值进行修改:
def test(): print(i) i= 2 i = 0 test()
报错:UnboundLocalError: local variable 'i' referenced before assignment
Python对局部作用域情有独钟,解释器执行到print(i),i在局部作用域没有。解释器尝试继续执行后面定义了名字i,解释器就认为代码在定义之前就是用了名字,所以抛出了这个异常。如果解释器解释完整个函数都没有找到名字i,那就会沿着搜索链LEGB往上找了,最后找不到抛出NameError异常。
是不是觉得另有所悟,对上面的代码稍作修改,能否推测出结果:
def test(): i = [2 , 2] i = [1 , 2] test() print(i) 输出结果: [1 , 2]
我想你应该猜到了结果,这个和上面的例子基本是一样的。再改一下:
def test(): i[0] = 2 i = [1 , 2] test() print(i)
输出结果:
[2, 2]
猜到了吗?是不是有些懵逼。list作为一个可变对象,l[0] = 2并不是对名字l的重绑定,而是对l的第一个元素的重绑定,所以没有新的名字被定义。因此在函数中成功更新了全局作用于中l所引用对象的值。
(7)情况7:请对比下面几种示例代码:
第一种:
i = 1 def f1(): print(i) def f2(): i = 2 f1() f2() print(i)
第二种:
i = 1 def f1(): print(i) def f2(): i = 2 return f1 ret = f2() ret() print(i)
第三种: