前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python编程思想(20):变量作用域

Python编程思想(20):变量作用域

作者头像
蒙娜丽宁
发布2020-06-18 16:35:15
5210
发布2020-06-18 16:35:15
举报
文章被收录于专栏:极客起源极客起源

在程序中定义一个变量时,这个变量是有作用范围的。变量的作用范围被称为它的作用域。根据定义变量的位置,变量分为如下两种:

  • 局部变量。在函数中定义的变量,包括参数,都被称为局部变量;
  • 全局变量。在函数外面、全局范围内定义的变量,被称为全局变量;

每个函数在执行时,系统都会为该函数分配一块“临时内存空间”,也可以称为函数栈,所有的局部变量都被保存在这块临时内存空间内。当函数执行完成后,这块内存空间就被释放了,这些局部变量也就失效了。因此离开函数之后就不能再访问局部变量了。

全局变量意味着它们可以在所有函数内被访问。不管是在函数的局部范围内还是在全局范围内,都可能存在多个变量,每个变量“持有”该变量的值。从这个角度来看,不管是局部范围还是全局范围,这些变量和它们的值就像一个“看不见”

的字典,其中变量名就是字典的key,变量值就是字典的 value。实际上,,Python提供了如下三个工具函数来获取指定范围内的“变量字典”。

  • globals:该函数返回全局范围内所有变量组成的“变量字典”。
  • locals:该函数返回当前局部范围内所有变量组成的“变量字典”
  • vars(object):获取在指定对象范围内所有变量组成的“变量字典”。如果不传入 object参数,vars()和 locals()的作用完全相同。

globals和 locals看似完全不同,但它们实际上也是有联系的,这两个函数的区别有如下两点:

  • locals()函数总是获取当前局部范围内所有变量组成的“变量字典”,因此,如果在全局范围内(在函数之外)调用 locals()函数,同样会获取全局范围内所有变量组成的“变量字典;而globals()函数无论在哪里执行,总是获取全局范围内所有变量组成的“变量字典“;
  • 一般来说,使用 locals()和 globals()获取的“变量字典”只应该被访问,不应该被修改。但实际上,不管是使用 globals()还是使用 locals()获取的全局范围内的“变量字典”,都可以被修改,而这种修改会真正改变全局变量本身,但通过 locals获取的局部范围内的“变量字典”,即使对它修改也不会影响局部变量;

下面的代码演示了如何使用 locals()函数和globals()函数访问局部范围和全局范围内的“变量字典”。

示例代码:locals_globals_test.py

代码语言:javascript
复制
def test ():
    n = 123
    # 直接访问n局部变量
    print(n) # 输出20
    # 访问函数局部范围的“变量数组”
    print(locals()) # {'n': 123}
    # 通过函数局部范围的“变量数组”访问n变量
    print(locals()['n']) # 123
    # 通过locals函数局部范围的“变量数组”改变n变量的值
    locals()['n'] = 321
    # 再次访问n变量的值
    print('n:', n) # 依然输出123
    # 通过globals函数修改x全局变量
    globals()['x'] = 30
x = 5
y = 33
print(globals()) # {..., 'x': 5, 'y': 33}
# 在全局访问内使用locals函数,访问的是全局变量的“变量数组”
print(locals()) # {..., 'x': 5, 'y': 33}
# 直接访问x全局变量
print(x) # 5
# 通过全局变量的“变量数组”访问x全局变量
print(globals()['x']) # 5
# 通过全局变量的“变量数组”对x全局变量赋值
globals()['x'] = 654
print(x) # 输出654
# 在全局范围内使用locals函数对x全局变量赋值
locals()['x'] = 555
print(x) # 输出555

运行这段代码,会输出如下的结果:

代码语言:javascript
复制
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fac501639d0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/System/Volumes/Data/MyStudio/python/python_knowledge/common_resources/books/我写的书/免费/Python编程思想/05-函数与lambda表达式/locals_globals_test.py', '__cached__': None, 'test': <function test at 0x7fac4000a170>, 'x': 5, 'y': 33}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fac501639d0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/System/Volumes/Data/MyStudio/python/python_knowledge/common_resources/books/我写的书/免费/Python编程思想/05-函数与lambda表达式/locals_globals_test.py', '__cached__': None, 'test': <function test at 0x7fac4000a170>, 'x': 5, 'y': 33}
5
5
654
555

从上面程序可以清楚地看出 locals函数用于访问特定范围内的所有变量组成的“变量字典”,而 globals函数则用于访问全局范围内的全局变量组成的“变量字典”

全局变量默认可以在所有函数内被访问,但如果在函数中定义了与全局变量同名的变量,此时就会发生局部变量遮蔽全局变量的情形。例子代码如下:

代码语言:javascript
复制
name = 'Mike'
def test1 ():
    # 直接访问name全局变量
    print(name) # MIke
    name = '钢铁侠'
test1()
print(name)

上面程序中直接访问name变量,这是允许的,此时程序将会输出Mike。如果在test1函数最后加如下一行代码:

代码语言:javascript
复制
name='钢铁侠'

再次运行该程序,将会看到如下错误。

代码语言:javascript
复制
UnboundLocalError: local variable 'name referenced before assignment

该错误提示所访问的name变量还未定义。这是什么原因呢?这正是由于程序在test1()函数中增加了“name=钢铁侠”一行代码造成的。Python语法规定:在函数内部对不存在的变量赋值时,默认就是重新定义新的局部变量。因此这行代码相当于重新定义了name局部变量,这样name全局变量就被遮蔽了,所以这段代码就会报错。

为了避免这个问题,可以通过两种方式来修改上面程序。

1.访问被遮蔽的全局变量

如果程序希望print(name)依然能访问name全局变量,且在可以在该行代码之后可重新定义name局部变量,此时可通过 globals函数来实现,将上面程序改为如下形式即可。

代码语言:javascript
复制
name = 'Mike'
def test ():
    # 直接访问name全局变量
    print(globals()['name'])  # Mike
    name = '钢铁侠'
test()
print(name)  # Mike

2.在函数中声明全局变量

为了避免在函数中对全局变量赋值(不是重新定义局部变量),可使用 global语句来声明全局变量。因此,可将程序改为如下形式。

代码语言:javascript
复制
name = 'Mike'
def test ():
    # 声明name是全局变量,后面的赋值语句不会重新定义局部变量
    global name
    # 直接访问name全局变量
    print(name)  # Mike
    name = '钢铁侠'
test()
print(name)  # 钢铁侠

增加了“ global name”声明之后,程序会把name变量当成全局变量,这意味着 test()函数后面对name赋值的语句只是对全局变量赋值,而不是重新定义局部变量。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客起源 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档