python 中的作用域scope 与 LEGB

参考:
Scope

scope(作用域)

以我目前查到的资料来说,scope还是翻译成作用域比较好,但是作用域总是让我想到它是在特指某一个变量的作用域,而不是一个大的范围。但是翻译成范围,又感觉不是很贴切。

然后结合python中存在着局部变量,全局变量等等,每个变量有它的生效范围,所以还是理解为作用域较好。

那么,什么是scope(作用域)?

当我们在程序中定义一个变量,一个函数,或者一个类的时候,它们总是只能在一个确定的区域内才可以访问到。在这个区域内,我们可以用某一个名称(变量名)来标识一个对象,一个函数等,这个确定的区域叫做scope(作用域)。根据变量或者函数名的定义的位置,这个scope(作用域)的范围可以从 单独代码块(如函数) 扩展到整个运行环境中。
参考:What is a scope in Python?

scope(作用域)限定了python中对象的可访问性,要访问代码中的特定变量,必须定义scope(作用域),因为不能让该变量在程序中的任何位置都可以访问,这个变量可见的特定代码区域叫做scope(作用域)。
变量不是对整个代码区都可见的,我们可以限制它的可见性。作用域就限定了哪些变量可见。
scope(作用域)定义了一套规则,这个规则告诉我们,如何搜索变量以及去哪里搜索。对于搜索到的变量,我们可能会访问它(使用它的值)或者赋予新值。
参考:Introduction to Scope in Python

在这里插入图片描述

代码实例:
import math

PI = math.pi

def area(radius):
    theArea = PI * radius ** 2
    return theArea
    
def main():
    historyOfPrompts = []
    historyOfOutput = []
    
    def getInput(prompt):
        x = input(prompt)
        historyOfPrompts.append(prompt)
        return x
    
    def showOutput(val):
        historyOfOutput.append(val)
        print(val)
    
    rString = getInput('please enter radius of a circle: ')
    r = float(rString)
    val = area(r)
    showOutput('the area of the circle is :' + str(val) )

结合这个示例,理解LEGB

local scope(局部作用域)

局部作用域指定是 当前程序执行到的函数的作用域。
当你的程序执行到某一行代码时,那个包含着你那句代码的scope就叫做local scope(局部作用域)
我们可以理解理解为函数内部就是局部作用域。
比如说,当程序执行到函数showOutput()内部时

def showOutput(val):
    historyOfOutput.append(val)
    print(val)

这个函数的内部,就是一个局部作用域。
当你的代码使用了某个变量时,python首先会在local scope(局部作用域)中寻找这个变量。
python会去寻找 这个变量 在这个局部作用域内有没有被定义,定义的情况有下面三种(假设变量名叫 id):

  • 类似于 id = … 这种语句 有没有出现在当前的scope内,在这种情况下,在当前的scope内,id是一个对象的引用
  • 在当前的scope内,id 是一个函数的参数,这种情况下,id 是 对那个作为参数传递过来的对象的 引用
  • 在当前scope内 id 是一个 函数名 或者 是 类名,使用def 或者class 声明的

以上的id 指的是一个变量的名字,可以是其他的 变量名。

假如,当python 执行到这个函数时:

def showOutput(val):
	historyOfOutput.append(val)
	print(val)

val 被定义在局部作用域内(local scope),它是一个函数的参数。
python 在这个作用域scope内发现了变量val ,然后会在这个local scope内寻找它有没有被定义,它符合上面所说的三种定义方式中的一种:函数参数。
python在局部作用域找到了它的定义,他就是合法的,然后检索到val引用的对象
并返回。

Enclosing Scope 封闭作用域

封闭作用域 在我的理解就是 常用在嵌套结构中,比如这里的main()函数内部定义getInput() showOutput()函数,属于函数嵌套。
然后Enclosing scope 指的是外层函数的内部同时又是内层函数的外部 的那部分区域。

如果python在局部范围内,没有找到某一个变量id 的定义,它会去Enclosing scope(封闭作用域)内寻找,对于代码:

def main():
    historyOfPrompts = []
    historyOfOutput = []
    
    def getInput(prompt):
        x = input(prompt)
        historyOfPrompts.append(prompt)
        return x
    
    def showOutput(val):
        historyOfOutput.append(val)
        print(val)
    
    rString = getInput('please enter radius of a circle: ')
    r = float(rString)
    val = area(r)
    showOutput('the area of the circle is :' + str(val) )

main()函数的内部,showOutput()的外部(或者是getInput()函数的外部) 就属于Enclosing scope,在这个Enclosing scope 内部定义了:

historyOfPrompts, historyOfOutput, rString, r, val, getInput, and showInput 这些变量 

注意:函数名,也是一个变量

比如说在程序执行到historyOfOutput.append(val)处时,historyOfOutput处于showOutput()函数内,但局部作用域找不到它的定义。

由于在local scope范围内找不到它的定义,python接下来会去Enclosing scope中寻找有没有。

接着python来到了main()内部,它会在这里Enclosing scope寻找有没有类似 id = ....,或者idEnclosing function(这里就是指 是不是main函数的参数) ,或者idEnclosing scope 中的某一个函数名或者类名 。

python在Enclosing scope中找到了historyOfOutput的定义,因为有historyOfOutput = []这句代码,所以python会正常返回historyOfOutput指向的对象。

local scope是相对的

其实,所谓的loca scope 其实是 根据当前程序执行的位置来决定的。

当程序执行到showOutput()函数内部,这个函数内就是local scope ,当程序执行到getInput()函数内,这个内部就是local scope。

当程序执行在main()内部的historyOfPrompts处 或者在rString =...处,这时main内部就是local scope。

不同作用域中的相同变量名

在代码执行到showOutput()函数中时,函数内部有一个变量名叫val ,而在Enclosing scope中也有一个val,val = area(r)
当然,这是合理的,因为这是不同的scope,不同的scope中可以存在相同名字的变量,它们是互不影响的。
在代码执行到showOutput()中时local scope中的val是可见的,而Enclosing scope中的val是隐藏的,只有当python在local scope中 找不到val的定义语句,在会去Enclosing scope中找。

Global Scope

有一些变量,在程序的任何位置都可以读取到,这些变量所处的区域叫做全局作用域。这些变量可以在函数内部或者外部都可以访问。当我们想要在程序的其他地方使用这些相同的变量时,我们需要使用global关键字来声明。
对于这个示例来说:

import math

PI = math.pi

def area(radius):
    theArea = PI * radius ** 2
    return theArea
    
def main():
    historyOfPrompts = []
    historyOfOutput = []
    
    def getInput(prompt):
        x = input(prompt)
        historyOfPrompts.append(prompt)
        return x
    
    def showOutput(val):
        historyOfOutput.append(val)
        print(val)
    
    rString = getInput('please enter radius of a circle: ')
    r = float(rString)
    val = area(r)
    showOutput('the area of the circle is :' + str(val) )

定义了两个函数area()showOutput(),这两个函数内部都是局部作用域。
在两个函数之外,还定义了一个变量PI = math.pi,它所处的位置就是全局作用域。
在一个.py文件内部,在各种函数,类的外部定义的这种变量就是全局变量,它所处的范围就是全局作用域。

对于全局变量还是不建议使用太多,很容易导致一些命名冲突或者一些意想不到的结果。

global ,nonlocal 关键字,访问不需要加,修改需要。
命名空间geesforgeeks,命名空间与作用域之间的关系。

Built-In Scope

最后的作用域叫做Built-in scope,可以翻译为内嵌作用域。如果某一个变量,python解释器在local,Enclosing,global中都找不到,他就会去内嵌作用域中寻找。

比如,我们有下面的代码:

def f():
       x = int('6')
       print(x)


if __name__ == '__main__':
       f()

其中 x = int('6')int()函数在local scope 中没有定义,然后没有enclosing scope,接着是global scope中也没有定义,最后来到了,built-int scope中找。

built-in scope(内置作用域)包含了python中的内置的对象和函数的定义。在最近的python版本中使用内置模块( builtins module)实现的。

无论何时,当我们打开一个python解释器时,builtins module 会被自动的导入到我们的运行环境中。结果就是,我们可以访问到这个模块中所有的函数和对象,而且不需要import 他们。

像函数:

print(),abs(),input(),int(), float(), string(),sum(),max(),sorted() and other similar functions

都是定义在built-in scope中的,我们使用就不需要再导入了。

我们可以使用以下代码查看下在built-in scope中可以访问到的函数和对象:

builtin_names = dir(__builtins__)
for name in builtin_names:
    print(name)

在这里插入图片描述
所有的定义在builtins module中的标识名都是在built-in scope中的.

最后还有一篇写得很详细的关于python 中各种scope以及关键字global,nonlocal的教程:

Python Scope & the LEGB Rule: Resolving Names in Your Code

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值