首页 > 技术文章 > Python LEGB (Local, Enclosing, Global, Build in) 规则

zzyzz 2017-09-12 12:11 原文

 1       
       Local 一个函数定义了一个 local 作用域; PyFrameObject 中的 f_local 属性 2 Global 一个 module 定义了一个 global 作用域; PyFrameObject 中的 f_global 属性. 3 BuiltIn open, dir 的作用域等等, python 最顶层的作用域
4 Enclosing 5 例子, 6 b = 2 7 def funcO(): 8 b = 3 9 def funcI(): 10 print(b) 11 return funcI 12 13 f = funcO() 14 f() #1 15 16 Output, 17 3 #1 18 19 例子的输出结果是 '3' 而不是代码块儿最外层所定义的 'b = 2'. 20 f() 实际调用的内嵌在 funcO 中的 funcI 函数. funcI 位于 funcO 之内, 所以函数 funcI 的作用域内嵌于函数 funcO 的作用域内. 21 即, 函数 funcO 的作用域是内嵌函数 funcI 的'直接外围作用域', 所以例子的结果是 '3' 而不是 '2'. 22 单纯按照代码块儿的结构来说, 在 #1 处 'b = 3' 这个约束已经不再对 'f()' 这个调用起作用, 但是从打印的结果来看显然不是这样(依然起作用). 23 Python 虚拟机在执行 'f = funcO()' 的时候会执行 'def funcI():' (因为 函数 funcO return 了 funcI 函数对象), 24 就在这个时候 约束'b = 3' 与函数对象 funcI 捆绑了一起, 并把捆绑后的街哦过返回, 这个捆绑起来的整体被称为 '闭包'('捆绑' 一词可以理解为 '直接外围作用域' 的绑定过程). 25 闭包是 LEGB 规则中的 E -> enclosing 的首字母, 表示的是 '直接外围作用域' 这个概念. 26 27 global 关键字, 28 例子, 29 c = 1 30 def func1(): 31 print(c) 32 33 def func2(): 34 print(c) 35 c = 3 36 print(c) 37 38 func1() 39 func2() 40 41 Output, 42 1 # func1() 的打印 43 func2() # func2() 的打印 44 print(c) 45 UnboundLocalError: local variable 'c' referenced before assignment 46 47 func1 和 func2 同是对'直接外围作用域'的搜索, 为什么一个正确搜索约束 'c = 1',另一报错呢? 48 先来了解一个名字的定义 - '最内嵌套作用域规则'. 49 最内嵌套作用域规则:由一个赋值语句引进的名字在这个赋值语句所在的作用域里是可见(起作用)的, 50 而且在其内部嵌套的每个作用域里也可见,除非它被嵌套于内部的, 51 引进同样名字的另一条赋值语句所遮蔽/覆盖。 52 53 从 exception 中得知, 变量 c 没有被定义. 上述问题就出现在'定义'的后半句,‘除非’分句 - '除非它被嵌套于内部的, 54 引进同样名字的另一条赋值语句所遮蔽/覆盖。' 恰巧紧接着报错处, 通过赋值语句引进了一个同名约束('c = 3'),进而破坏了'最内嵌套作用域规则'. 55 56 现在尝试修改这个引用错误, 57 c = 1 58 def func1(): 59 print(c) #1 60 61 def func2(): 62 global c #2 在引用之前通过 global 关键字指定作用域 63 print(c) #2 64 c = 3 65 print(c) #3 66 67 func1() 68 func2() 69 print(c) #4 70 71 Output, 72 1 #1 73 1 #2 python 理解了编程者的意图 74 3 #3 75 3 #4 76 进一步再看一个闭包的例子, 77 d = 1 78 def func2(): 79 d = 3 80 def func2I(): 81 global d 82 print(d) #1 83 d += 4 #2 重写直接外层作用域 84 print(d) #2 85 return func2I 86 87 abc = func2() 88 abc() 89 print(d) #3 90 91 Output, 92 1 #1 global 关键字 打破 LEGB 规则, 限定引用直接外层作用于 93 5 #2 直接外层作用于被重写 94 5 #3 被重写的‘直接外作用域’(代码块儿最外层的 'd = 1' 这个约束)作用于 LEGB 规则下作用域

 

推荐阅读