python legb_Python 作用域和 LEGB

原标题:Python 作用域和 LEGB

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

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

名字和实体的绑定, 我们可以理解成赋值. 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:

classframe(object)

.....# 省略

|Data deors definedhere:

|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:

classcode(object)

......# 省略

|Data deors definedhere:

|

|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

deff():

a=6

printa# 输出 6

f()

printa# 输出 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

deff():

printa# 输出 3

printopen# 输出

f()

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

a=3

deff():

defv():

printa

returnv

test=f()

test()# 输出 3

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

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

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

a=3

deff():

a=4

defv():

printa

returnv

test=f()

test()# 输出 4 Why?

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

来源:Lin_R

my.oschina.net/u/2291453/blog/820865

程序员大咖整理发布,转载请联系作者获得授权

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Python函数中的变量作用域指的是变量的可见范围。在函数中定义的变量可以分为两种:局部变量和全局变量。 局部变量指的是在函数内部定义的变量,只能在函数内部使用,函数外部无法访问。当函数执行完毕后,局部变量的值会被销毁。 全局变量指的是在函数外部定义的变量,可以在函数内部和外部使用。但是,在函数内部如果要修改全局变量的值,需要使用global关键字进行声明。 在Python中,变量的作用域遵循LEGB规则,即:Local(局部变量)-> Enclosing(闭包函数外的函数中的变量)-> Global(全局变量)-> Built-in(内置变量)。 当函数内部使用变量时,Python会按照LEGB规则从内到外查找变量,直到找到为止。如果在函数内部没有找到变量,则会继续向外查找,直到找到为止。如果最终还是没有找到变量,则会抛出NameError异常。 因此,在编写Python函数时,需要注意变量的作用域,避免出现变量名冲突等问题。 ### 回答2: Python的函数中,变量的作用域并不像其他编程语言那样严格。在Python中,变量的作用域很容易受到内层作用域的影响,而无法访问外层的变量,这部分属于局部变量。下面我们从全局变量和局部变量两个方面来讲解变量的作用域。 一、全局变量的作用域Python中,如果变量未定义在任何函数内,即在全局作用域内,那么在各个函数内都可以访问该变量。 例如: ``` count = 0 def test(): global count count += 1 print(count) test() ``` 以上代码中,count变量未定义在函数内部,属于全局作用域,在调用函数`test()`时,可以使用`global`关键字来声明该变量为全局变量,然后在函数内部可以直接对该变量进行修改和访问。 二、局部变量的作用域Python中,如果变量定义在函数内部,则该变量的作用域只限于函数内部,外部无法访问该变量,称为局部变量。 例如: ``` def test(): count = 0 count += 1 print(count) test() ``` 以上代码中,count变量定义在函数`test()`内部,属于局部变量。在函数内部对count进行修改和访问也是可以的,但是在函数外部是无法访问到该变量的,否则会报错。 需要注意的是,函数内的变量名如果和全局变量的变量名相同,那么在函数内访问该变量时,默认会访问局部变量,而非全局变量。如果仍要在函数内部访问全局变量,可以使用`global`关键字进行声明。 例如: ``` count = 0 def test(): count = 1 print("count in local:", count) test() print("count in global:", count) ``` 以上代码中,函数内部定义了一个名为count的局部变量,调用函数后,输出的是局部变量count的值,而不是全局变量count的值0。如果要访问全局变量count的值,可以在函数内部使用`global count`声明该变量为全局变量,再进行访问。 总之,Python的变量作用域相对比较宽松,可以根据具体情况进行灵活使用,但是在使用局部变量和全局变量时要避免命名冲突,同时合理使用`global`关键字来声明全局变量,以免出现意想不到的错误。 ### 回答3: 在Python中,变量的作用域指的是变量所能被访问到的范围。在一个函数中定义的变量只能在函数内部被访问到,而在函数外定义的变量则可以在整个程序中被访问到。 Python中的变量作用域分为两种:局部作用域和全局作用域。局部变量指的是在一个函数内部定义的变量,只能在该函数内部访问。全局变量指的是在函数外部定义的变量,可以在整个程序中被访问到。如果在函数内部要访问全局变量,则需要使用global关键字进行声明。 在Python中,变量的作用域可以遵循 LEGB 原则,即 Local(局部)、Enclosing(闭包)、Global(全局)、Built-in(内置)的顺序进行查找。这意味着变量首先在函数内部被查找,然后在函数外部被查找,之后在内置变量中被查找。 当在函数内部定义与全局变量同名的变量时,Python会优先使用局部变量而不是全局变量。如果需要在函数内部修改全局变量,则必须使用global关键字声明。 在使用闭包时,可以通过在函数内部再定义一个函数,内部函数可以访问外部函数中的变量。这样的变量作用域称为嵌套作用域。在Python中,使用nonlocal关键字可以实现在内部函数中修改外部函数中定义的变量。 总之,学习变量的作用域对于编写规范化的程序来说非常重要,特别是在编写复杂的函数时。了解变量作用域可以帮助我们更好地管理变量,并避免不必要的错误和问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值