参考:
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 = ....
,或者id
是Enclosing function
(这里就是指 是不是main
函数的参数) ,或者id
是Enclosing 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的教程: