Python中命名空间与作用域使用总结(3)

  推测出输出结果了吗?没错,会报错: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)情况4

list_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)

  第三种:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/5424570c827e43670b8e3a8ea68affcb.html