文档章节

Python 作用域(scope) 和 LEGB

Lin_R
 Lin_R
发布于 2017/01/08 13:24
字数 1608
阅读 240
收藏 5

约束 名字空间 作用域 之间的那些事

不管在什么编程语言, 都有作用域这个概念.作用域控制在它范围内代码的生存周期, 包括名字和实体的绑定.

名字和实体的绑定, 我们可以理解成赋值. num = int_obj, 当我们执行这句代码时, 实际上我们已经得到一个('num', int_obj)的关联关系, 我们也能将称之为约束, 这个约束也将存在名字空间(name space)里面, 名字空间也将是LEGB查找的依据.

而每个名字空间, 也将对应一个作用域, 作用域是代码正文中的一段代码区域, 作用域的有效范围更多是这段代码区域去衡量,一个作用域可以有多个名字空间, 一个名字空间也能有多个约束(多个赋值语句)

可以通过sys._getframe().f_code.co_name 查看代码所处的作用域, 先来看下sys._getframe是什么鬼吧?

# sys module
def _getframe(depth=None): # real signature unknown; restored from __doc__
    """
    _getframe([depth]) -> frameobject
    
    Return a frame object from the call stack.  If optional integer depth is
    given, return the frame object that many calls below the top of the stack.
    If that is deeper than the call stack, ValueError is raised.  The default
    for depth is zero, returning the frame at the top of the call stack.
    
    This function should be used for internal and specialized
    purposes only.
    """
    pass

从函数的定义可以看到, sys._getframe将返回一个frameobject对象, 那其实frameobject是什么对象? 为什么它能决定作用域?

frameobjec实际上就是python虚拟机上所维护的每个栈帧, 这和我们常规理解的栈帧多点差别, 因为python在原有栈帧的基础上, 在封装一层形成自己的栈帧. 虽然是有些不同, 但是我们还是能近似看成常规理解的栈帧, 包括入栈,出栈 局部变量等等

那么frameobejct里面究竟有什么? 

# help(sys._getframe())
# Output:
class frame(object)
 .....            # 省略
 |  Data descriptors defined here:
 |  f_back        # 上一个栈帧对象(谁调用自己)
 |  f_builtins    # 内置名字空间
 |  f_locals      # 全局名字空间
 |  f_globals     # 全局名字空间
 |  f_code        # 帧指向的 codeObject对象
 .....            # 省略

我们现在已经知道frameobject的来历呢, 那么再回顾上面提到的: sys._getframe().f_code.co_name

毫无疑问, 我们还是得看下codeobject是什么东西, 才能知道name的意思:

同样也是print help大法

# print help(sys._getframe().f_code)
# Output:
class code(object)
 ......        # 省略
 |  Data descriptors defined here:
 |  
 |  co_name    # code block的名字, 通常是类名或者函数名 /* string (name, for reference) */ 
 |  
 |  co_names   # code block中所有的名字 /* list of strings (names used) */
 |
 ......        # 省略

虽然 sys._getframe().f_code.co_name 顶多也只能说明, 这段代码是在哪个code block里面, 并没有直接证明就是作用域, 但是从上面也已经谈到, 作用域是从代码正文的代码片段的决定, So, 也能近似看成算是作用域的名字了~

作用域话题似乎聊得有点深入了, 让我们暂告一段落, 继续讲讲 约束 和 作用域的关系吧

每个约束一旦创建, 将会持续的影响后面代码的执行, 但是约束也只能在名字空间内生效, 也就是说,一旦出了名字空间/作用域. 约束也将失效

a = 3
def f():
    a = 6
    print a    # 输出 6
f()
print a        # 输出 3

在上面例子可以看到, 变量a在模块层和函数f层都有赋值, 在执行函数f时,输出6, 但是在下面却输出了3, 也就是因为函数f 中的 a=3 约束只有在函数f的作用域中生效,函数结束,a的值, 应该是最开始的a=3来控制, 我们现在应该隐约有种感觉, 为什么赋值语句会被称为约束? 我们完全可以理解成, 一个变量名, 可能有多次改变其绑定的实体对象的机会, 但是最终显示是哪个实体, 完全就是从作用域->名字空间->约束 来决定

LEGB

从上面我们已经清楚 约束,名字空间, 作用域之间微妙的关系, 那么我们接下来就应该探讨下变量查找的方式了.

LEGB 分别是:

  • locals 是函数内的名字空间,包括局部变量和形参
  • enclosing 外部嵌套函数的名字空间(闭包中常见)
  • globals 全局变量,函数定义所在模块的名字空间
  • builtins 内置模块的名字空间

而查找的优先顺序从左到右以此是: L -> E -> G -> B

从上面我们已经知道, 约束, 是受作用域和名字空间的影响, 所以查找肯定也是只能在名字空间去进行

来些简单代码吧:

a = 3
def f():
    print a     # 输出 3
    print open  # 输出 <built-in function open>
f()

print '----------------------分割线----------------'

a = 3
def f():
    def v():
        print a
    return v
test = f()
test()          # 输出 3

这段相信大家都知道为什么能够输出3, 当在函数内部的名字空间找不到关于变量a的约束时, 将会去全局变量的名字空间查到, OK, 已经找到了 (a,3)的约束, 返回 3., test()也是同理

同样的, 在函数内部和模块内部都不能找到open的约束, 那么只能去Bulitin(内置名字空间)去查找了, 找到了open了, 并且还是个函数, 所以返回 <built-in function open>

简单的演示完, 来些神奇的代码:

a = 3
def f():
    a = 4
    def v():
        print a
    return v
test = f()
test()     # 输出 4 Why?

有没有觉得很奇怪, a=4是在函数f里面定义的, 但是返回v的时候, 函数已经退出,理应释放了, 为什么test()还能输出4呢? 其实原因很简单, 首先这个已经是闭包函数了, 同样的还是遵循LEGB的原则, 函数v已经能够在外层嵌套作用域找到a的定义, 又因为闭包函数有个特点, 在构建的时候, 能够将需要的约束也一并绑定到自身里头, 所以即使函数f退出了, 变量a释放了, 但是不要紧, 函数v已经绑定好了相应的约束了, 自然而然也就能输出4

欢迎各位大牛指点教育, 转载请注明:https://my.oschina.net/u/2291453/blog/820865

© 著作权归作者所有

Lin_R
粉丝 42
博文 18
码字总数 20310
作品 0
广州
程序员
私信 提问
python——作用域之LEGB规则

1 变量的作用域   Python是静态作用域,也就是说在Python中,变量的作用域源于它在代码中的位置;在不同的位置,可能有不同的命名空间。命名空间是变量作用域的体现形式。 2 LEGB各自代表的...

翠竹09
2018/08/12
0
0
给妹子讲python-S01E18初探函数作用域

当你在一个程序中使用变量名时,python创建、改变或查找变量名都是在所谓的命名空间中进行的,也就是我们要说的变量的作用域。在代码中给一个变量赋值的地方决定了这个变量将存在于哪一个命名...

技术小能手
2018/09/05
0
0
Python中的变量作用域与命名空间(LEGB)

Python 使⽤LEGB的顺序来查找⼀个符号对应的对象 locals -> enclosing function -> globals -> builtins 局部变量———闭包空间———全局变量———内建模块 a = 1 # 全局变量 globals de...

qq_39521554
2018/04/16
0
0
Python 的作用域的相关规则

我并不喜欢 Python 的作用域的设计,但这门语言是如此流行,以至于很多时候你不得不去了解它. 本文试图比较全面地总结 Python 的作用域的相关规则. 若本文有错误之处,欢迎纠正. 本文基于 Py...

急速奔跑中的蜗牛
06/09
0
0
【函数】02、函数进阶

一、函数的返回值 In [4]: def add(x, y): ...: return x+y ...: In [5]: add(1, 2)Out[5]: 3 In [8]: def add(x, y): ...: return x+y ...: print(x+y) ...: In [9]: add(1, 2)Out[9]: 3 In......

xiexiaojun
2017/06/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

parseint和isNaN用法

本文转载于:专业的前端网站➭parseint和isNaN用法 <!doctype html><html><head><meta charset="utf-8"><title>无标题文档</title></head><body><script> var a='12'; alert......

前端老手
37分钟前
6
0
Kylin 精确去重在用户行为分析中的妙用

作者:史少锋,Apache Kylin committer & PMC,2019/10/11 在上次文章《如何在 1 秒内做到大数据精准去重》中,我们介绍了 Apache Kylin 为什么要支持大数据集上的精确去重,以及基于 Bitmap...

ApacheKylin
48分钟前
5
0
学习记录(二) es6基本语法(rest参数,模板化,axios模块,拦截器)

日常学习记录 模块化:把一个大文件分成多个小文件,按照一定规范进行拼接 es5写法: 导出:module.exports = 数据 导入:require("路径") /路径未添加后缀名时 //默认添加.js //把路径作为文件名...

Pole丶逐
52分钟前
4
0
以程序员的角度怎么购买一台「性价比高的电视」

前俩天有小伙伴在我的文章下留言,说能否把 【国内电视机都介绍一下】,今天我已在TV端开发多年的程序员的角度。谈谈已程序员的角度如何购买一台性价比高的电视。 国内大的电视机品牌介绍 长...

我们都很努力着
55分钟前
3
0
PhotoShop 色调:理解直方图/RGB通道信息

一、直方图:图表的形式,展示图像像素分布的情况 1.平均值:表示平均亮度 2.标准偏差值:表示亮度值范围内的中间值 3.像素: 表示用于计算直方图的像素总数 4.色阶:显示指针下面的区域亮度...

东方墨天
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部