专栏首页极客起源Python编程思想(20):变量作用域

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

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

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

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

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

的字典,其中变量名就是字典的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

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

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

{'__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函数则用于访问全局范围内的全局变量组成的“变量字典”

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

name = 'Mike'
def test1 ():
    # 直接访问name全局变量
    print(name) # MIke
    name = '钢铁侠'
test1()
print(name)

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

name='钢铁侠'

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

UnboundLocalError: local variable 'name referenced before assignment

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

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

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

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

name = 'Mike'
def test ():
    # 直接访问name全局变量
    print(globals()['name'])  # Mike
    name = '钢铁侠'
test()
print(name)  # Mike

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

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

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

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

本文分享自微信公众号 - 极客起源(geekculture),作者:geekori

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python编程思想(30):用 metaclass搞定一批类的特性

    如果希望创建某一批类全部具有某种特征,则可以通过 metaclass来实现。使用 metaclass可以在创建类时动态修改类定义。为了使用 metaclass动...

    蒙娜丽宁
  • Python编程思想(26):成员变量

    李宁老师已经在「极客起源」 微信公众号推出《Python编程思想》电子书,囊括了Python的核心技术,以及Python的主要函数库的使用方法。读者可以在「极客...

    蒙娜丽宁
  • 用Linux感觉低效吗?来看看这几个技巧!

    Linux已经成为目前最火的操作系统之一,尽管现在的Linux用户很多,但很多使用Linux的同学发现,他们在Linux下的工作效率并不高,那么这是为什么呢?其...

    蒙娜丽宁
  • 关于网站SEO优化,我只做四件事

    在SEO网站优化的过程中,多数人讲网站优化,就是讲网站的架构,关键字,标题,描述,dofollow,nofollow,sitemap,伪静态,H标签,关键字密度...

    神勇大师兄
  • pywin32模拟鼠标键盘操作

    第三个参数:函数操作的一个标志位,如果值为KEYEVENTF_EXTENDEDKEY则该键被按下,也可设置为0即可,如果值为KEYEVENTF_KEYUP则该按...

    周小董
  • 6.6 局部变量和全局变量

    ④在一个函数内部,可以在复合语句中定义变量,这个变量只在本复合语句中有效,这种 复合语句也称为“分程序”或“程序块”

    闫小林
  • 7.4 局部变量和全局变量

    2、在一个函数内部定义的变量只在本函数范围内有效,也就是说只有在本函数内才能引用它们,在此函数以外是不能使用这些变量的。

    闫小林
  • 每天 3 分钟,小闫带你学 Python(二十二)

    Often when you think you're at the end of something, you're at the beginning of ...

    小闫同学啊
  • 干货|网站为什么要做优化?应该怎么做?

    互联网的高速发展,推动各中小企业也纷纷投入线上营销的新销售渠道之中,开始建立自己企业网站。但企业主往往缺乏专业的网站推广人才和知识,于是不少企业会直接在网络上找...

    耐思智慧
  • 关于Python局部变量和全局变量必须知道的几句话

    虽然Python支持非常复杂的变量作用域和访问顺序(详见:几行代码理解Python变量访问的LEGB顺序、详解Python变量作用域),但是一般而言,能够分清局...

    Python小屋屋主

扫码关注云+社区

领取腾讯云代金券