变量作用域

python的域规则

-变量作用域:在Python程序中创建、改变、查找变量名时,都是在一个保存变量名的空间中进行,我们称之为命名空间,也被称之为作用域。Python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在源代码中的位置决定。不同的作用域可以存在相同的变量名。

例1:

a = 30
def ex():
    b = 25
    return a+b

a是全局变量,b是局部变量;函数可以对a,b进行访问,代码的主体部分只能访问a。


例2:

globalVar = 100           #全局作用域

def test_scope():
    enclosingVar = 200    #嵌套作用域
    def func():
        localVar = 300    #局部作用域
print __name__            #内置作用域

当搜索一个标识符时,python会先从局部作用域开始查找,如果找不到则到上一层嵌套结构中def或lambda函数的嵌套作用域中继续寻找,之后是全局作用域,最后是内置作用域。按这个查找原则,在第一处找到的地方停止。如果还未找到,抛出异常NameError。
搜索变量名的优先级:局部作用域 > 嵌套作用域 > 全局作用域 > 内置作用域

下例中text的作用域实在函数内部,在全局中是无法调用的。

>>> def func():
    text = 'hello world'
>>> print(text)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
NameError: name 'text' is not defined

作用域的类型

在Python中,使用一个变量时并不严格要求需要预先声明它,但是在真正使用它之前,它必须被绑定到某个内存对象(被定义、赋值);这种变量名的绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量。

  • 局部作用域:
    局部变量:包含在def关键字定义的语句块中,即在函数中定义的变量。每当函数被调用时都会创建一个新的局部作用域。Python中也有递归,即自己调用自己,每次调用都会创建一个新的局部命名空间。在函数内部的变量声明,除非特别的声明为全局变量,否则均默认为局部变量。有些情况需要在函数内部定义全局变量,这时可以使用global关键字来声明变量的作用域为全局。局部变量域就像一个 栈,仅仅是暂时的存在,依赖创建该局部作用域的函数是否处于活动的状态。所以,一般建议尽量少定义全局变量,因为全局变量在模块文件运行的过程中会一直存在,占用内存空间。
    注意:如果需要在函数内部对全局变量赋值,需要在函数内部通过global语句声明该变量为全局变量.
# 通过global语句在函数体中重新赋值全局变量
>>> global_str = '100'
>>> def func():
...     global global_str
...     global_str = '500'
...     local_str = '200'
...     return local_str + global_str
...
>>> func()
'200500'
>>> print(global_str)   #全局变量被重新赋值了
500

#不使用global语句
>>> global_str = '100'
>>> def func():
...     global_str = '500'
...     local_str = '200'
...     return local_str + global_str
...
>>> func()
'200500'
>>> print(global_str)    #这里全局变量的值,没有改变
100
  • 嵌套作用域:python闭包函数
    对一个函数而言,局部作用域是定义在此函数内部的局部作用域,而嵌套作用域是定义在此函数的上一层父级函数的局部作用域。主要是为了实现Python的闭包,而增加的实现。
variable = 100
def test_scopt():
    variable = 200
    print variable
    def func():
        print variable   #这里的变量variable在嵌套作用域中绑定了内存对象200,为函数func()引入了一个新的变量
    func()
test_scopt()
print variable

运行结果为:
200    # 嵌套作用域print的值
200    # func()函数局部作用域无法找到variable的值,向父级test_scopt()寻找
100    # 无法访问局部作用域,打印全局变量的值
  • 全局作用域
    即在模块层次中定义的变量,每一个模块都是一个全局作用域。也就是说,在模块文件顶层声明的变量具有全局作用域,从外部开来,模块的全局变量就是一个模块对象的属性。
    注意:全局作用域的作用范围仅限于单个模块文件内。

  • 内置作用域
    系统内固定模块里定义的变量,如预定义在builtin 模块内的变量。

对变量的修改

一个非局部变量相对于局部变量而言,默认是只读而不能修改的。如果希望在局部变量中修改定义在非局部变量的变量,为其绑定一个新的值,Python会认为是在当前的局部变量中引入一个新的变量(即便内外两个变量重名,但却有着不同的意义)。即在当前的局部变量中,如果直接使用非局部变量中的变量,那么这个变量是只读的,不能被修改,否则会在局部变量中引入一个同名的新变量。

  • 可以通过nonlocal和global等关键字修改非局部变量的值
# 直接修改实质是定义了一个新的变量
>>> global_str = '100'
>>> def func():
...     global_str = '500'   #引入一个新变量,覆盖全局变量中的变量
...     local_str = '200'
...     return local_str + global_str
...
>>> func()
'200500'
>>> print(global_str)    #这里全局变量的值,没有改变
100

global语句

num = 100
def test():
    def nested():
        global num             # 在局部作用域中修改全局变量的值
        print('current=', num)
        num = 520
    return nested

test()()
print(num)

输出结果:
current= 100
520

注意:test()()表示会自动调用函数test()的返回值,且此返回值必须为可调用类型,即存在__ call__方法。返回一个函数,所以也会执行返回的函数体代码。

nonlocal关键字(python3.x新添加的关键字)

def outer():
    count = 10
    def inner():
        nonlocal count
        count = 20
        print(count)
    inner()
    print(count)
outer()

输出结果:
20
20

命名空间和作用域

  • 命名空间:是一个包含了该空间中所有变量和变量值的字典,不同的命名空间之间是相互隔离的,所以在不同的命名空间可以创建同名变量,通过句点标识符来调用和区别,例如:student.name和teacher.name是两个不同的命名空间。Python 内置了两个查询命名空间的字典的内置函数:globals()、locals():
# 在全局环境中调用,所以locals()和globals()返回值一致。
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}

>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}

注意:

  1. 根据调用地方的不同,globals()和locals()函数可被用来返回全局和局部命名空间里的名字。
  2. 如果在函数内部调用locals(),返回的是所有能在该函数里访问的命名。
  3. 如果在函数内部调用globals(),返回的是所有在该函数里能访问的全局名字。
  4. 两个函数的返回类型都是字典。所以能用keys()函数摘取变量名。
  • 作用域:是一个变量能够有效的区域,全局作用域的全局变量在整个模块中有效,局部作用域中的局部变量只在类或函数中有效。创建一个作用域会同时生成一个命名空间,并且作用域包围了其命名空间。作用域是为了实现变量查询的路径,就如上文所述,如何局部作用域中含有于全局作用域同名的变量时,局部作用域会屏蔽掉全局作用域,这是因为变量的查询路径中,局部作用域要先于全局作用域,然后再到相对的命名空间中获取变量的值。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,176评论 5 469
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,190评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,232评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,953评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,879评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,177评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,626评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,295评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,436评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,365评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,414评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,096评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,685评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,771评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,987评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,438评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,032评论 2 341

推荐阅读更多精彩内容