四个函数搞清 Python 作用域

简介: 四个函数搞清 Python 作用域

简说Python,号主老表,Python终身学习者,数据分析爱好者,从18年开始分享Python知识,原创文章227篇,写过Python、SQL、Excel入门文章,也写过Web开发、数据分析文章,老表还总结整理了一份2022Python学习资料和电子书资源,关注后私信回复:2022 即可领取。

四个函数搞清 Python 作用域


Python 内置函数中,和作用域相关的函数有 locals、global、vars 以及 dir。日常常用这些函数,但时常分不清准确用法,试来试去。下面来分析这些函数是什么意思,有什么差别,一劳永逸学会 Python 作用域的相关知识点。

本文先说明这些函数最直接的功能,然后对特殊情况进行解释,最后说明这些函数有什么用。

最直接的功能

最直接和常用的功能如下:

  • locals() 返回当前局部作用域的符号表字典。
  • globals() 返回全局作用域的符号表字典。
  • vars() 相当于 locals ()
  • dir() 返回当前局部作用域的符号表字典对应的 keys()。

来看代码。

>>> import time
>>> pi = 3.1415
>>> globals()
{'__spec__': None, 'pi': 3.1415, 'time': <module 'time' (built-in)>, '__loader__': <class'_frozen_importlib.BuiltinImporter'>, '__name__': '__main__', '__doc__': None, '__package__': None, '__builtins__': <module 'builtins' (built-in)>}

这里导入了 time 模块,定义了 pi 全局变量。调用 globas () 输出全局变量,可见是一些系统相关的内置属性,以及定义的 pi 和导入的 time 。

出于篇幅和方便阅读考虑,之后输出都不再列出 __ 开头的属性。上例输出,去掉 __ 开头属性为:{'pi': 3.1415, 'time': <module 'time' (built-in)>},阅读更清晰。

继续调用 locals() 函数,输出当前局部作用域的变量表。因为 locals() 位于交互 REPL 界面,所以当前局部作用域实际就是全局作用域,所以两者输出一样。

>>> locals() #去掉 __ 开头的属性。
{'pi': 3.1415, 'time': <module 'time' (built-in)>}

继续,因为 vars() 相当于 locals(),所以代码globals() == locals() == vars()成立,结果为 True 。

最后调用 dir(),返回当前局部作用域的符号表字典对应的 keys()

>>> dir() #去掉 __ 开头的属性
['pi', 'time']

因为 dir() 返回的是局部变量字典的 keys,又因为这里局部变量和全局变量一致,加上 vars() 又相当于 locals(),所以实际上,可以得到如下等式。

>>> sorted(dir()) == sorted(globals().keys()) == sorted(locals().keys()) == sorted(vars().keys())
True

目前为止的分析中,locals 和 globals 都在全局作用域中,vars 和 dir 都没有参数,下面分析特殊情况。

特殊情况

先来分析当 locals 和 globals 位于函数内部时的情况。

locals 和 globals 位于函数内部

看如下代码

zs = 'zhangsan'
def hello(name):
    print(globals())
    print(locals())
    print(vars())
    print(dir())
    print('Hello,%s!'%name ) 
ls = 'lisi'
hello(ls)

在函数内部,global() 返回的是全局作用域变量,所以 zs,ls 和 hello 函数都有。而 locals() 返回局部变量和自由变量,这里没有闭包,只有函数形参 name。vars() 和 dir() 和上例相同,结果如下:

{'ls': 'lisi', 'zs': 'zhangsan', 'hello': <function hello at 0x00667DB0>} 
{'name': 'lisi'}
{'name': 'lisi'}
['name']
Hello,lisi!

另外,确切地说,globals() 返回的是函数定义时的全局变量空间。所以,如果 zs 变量和 hello 函数定义在另一个文档 lib.py 中,在本程序中 from lib import hello 后,再执行,返回的是定义时的全局变量:{'zs': 'zhangsan', 'hello': <function hello at 0x00667DB0>}不会包含调用空间的全局变量 ls。

下面说明当 vars 和 dir 有参数时的情况。

vars 和 dir 带参数执行

vars 和 dir 函数有参数时的功能如下:

  • vars(obj) 返回 obj 的 dict
  • dir(obj) 返回 obj 的属性。

vars(obj) 返回模块、类、实例以及有 dict 属性的对象的 dict 属性。

模块、类以及实例中的 dict 指代模块的命名空间或属性字典。

如下代码,新建一个类和类的实例,分别对类和实例进行 vars 操作,可见:

class Man:
    def __init__(self,age,name):
        self.age = age
        self.name = name
zs = Man(21,'zhangsan')
print(vars(Man))
print(vars(zs))

这其中,类返回的是编译相关的对象,诸如弱引用、模块名、文档等。对象返回的是属性,这里是对象属性 age 和 name。

{'__weakref__': <attribute '__weakref__' of 'Man' objects>, '__dict__': <attribute '__dict__' of 'Man' objects>, '__module__': '__main__', '__doc__': None, '__init__': <function Man.__init__ at 0x00657390>}
{'age': 21, 'name': 'zhangsan'}

dir(obj) 返回对象的有效属性的名字,这个有效属性根据模块、类和对象等有所不同,大概是从 dict 中提取的,但又不全然这样。

虽然常在交互模式下 dir() 查询对象,但这查询并不完全。

看实例和类的差别。

print(set(dir(zs))-set(dir(Man)))

以上代码对 zs 实例和 Man 类的 dir() 执行结果求差集,可知对象实例与类实例相比,多了 {'age', 'name'} 两个属性,这也是 zs 实例的应有之义。

分析了半天的 globals、locals、vars和 dir,它们有什么用呢?

作用

了解、分析对象用法

我们常在 REPL 命令行中用 dir() 来查看类或对象支持的方法、拥有的属性,这在认识新包、编写程序需要提示时比较有作用。比如 import time 后,忘记 time 的方法了,可以 dir(time) 一下。

globals 和 locals 可以帮助认识函数的作用域,了解当前作用域的变量,分析局部、全局、自由变量。

另外,借助于 globals 和 Locals 可以实现动态编写代码。

动态编写代码

如下代码,用 exec 动态执行 Python 代码,生成新的变量 s。这里 exec 第一个参数时源代码,第二个参数是源代码的全局变量环境,第三个参数是源代码的局部变量环境。

取当前全局变量字典(为了用全局变量 math 中的 pi)做第二个参数。

取当前局部变量字典,并稍作修改,加入 r 局部变量,形成 loc 字典做第三个参数。

import math 
loc = locals()
loc['r'] = 10
print('exec',exec('s = math.pi * r * r'),globals(),loc)
print(s)
print(locals()['s'])

可得计算结果 314.159。用 print(s) 输出,说明这里的 s 是动态生成的。又用 locals()['s'] 输出,说明这里的 s 赋值给了局部作用域字典。

总结

本文先说明 globals ()、locals()、vars()、dir() 这些函数最直接的功能,然后对特殊情况进行解释,最后说明这些函数在理解变量作用域、分析对象、动态编码中的作用。

相关文章
|
2月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
170 1
|
2月前
|
算法 Java Docker
(Python基础)新时代语言!一起学习Python吧!(三):IF条件判断和match匹配;Python中的循环:for...in、while循环;循环操作关键字;Python函数使用方法
IF 条件判断 使用if语句,对条件进行判断 true则执行代码块缩进语句 false则不执行代码块缩进语句,如果有else 或 elif 则进入相应的规则中执行
266 1
|
2月前
|
Java 数据处理 索引
(numpy)Python做数据处理必备框架!(二):ndarray切片的使用与运算;常见的ndarray函数:平方根、正余弦、自然对数、指数、幂等运算;统计函数:方差、均值、极差;比较函数...
ndarray切片 索引从0开始 索引/切片类型 描述/用法 基本索引 通过整数索引直接访问元素。 行/列切片 使用冒号:切片语法选择行或列的子集 连续切片 从起始索引到结束索引按步长切片 使用slice函数 通过slice(start,stop,strp)定义切片规则 布尔索引 通过布尔条件筛选满足条件的元素。支持逻辑运算符 &、|。
162 0
|
3月前
|
设计模式 缓存 监控
Python装饰器:优雅增强函数功能
Python装饰器:优雅增强函数功能
270 101
|
3月前
|
缓存 测试技术 Python
Python装饰器:优雅地增强函数功能
Python装饰器:优雅地增强函数功能
214 99
|
3月前
|
存储 缓存 测试技术
Python装饰器:优雅地增强函数功能
Python装饰器:优雅地增强函数功能
188 98
|
3月前
|
缓存 Python
Python中的装饰器:优雅地增强函数功能
Python中的装饰器:优雅地增强函数功能
|
4月前
|
Python
Python 函数定义
Python 函数定义
519 155
|
3月前
|
算法 安全 数据安全/隐私保护
Python随机数函数全解析:5个核心工具的实战指南
Python的random模块不仅包含基础的随机数生成函数,还提供了如randint()、choice()、shuffle()和sample()等实用工具,适用于游戏开发、密码学、统计模拟等多个领域。本文深入解析这些函数的用法、底层原理及最佳实践,帮助开发者高效利用随机数,提升代码质量与安全性。
644 0
|
4月前
|
数据挖掘 数据处理 C++
Python Lambda:从入门到实战的轻量级函数指南
本文通过10个典型场景,详解Python中Lambda匿名函数的用法。Lambda适用于数据处理、排序、条件筛选、事件绑定等简洁逻辑,能提升代码简洁性和开发效率。同时提醒避免在复杂逻辑中过度使用。掌握Lambda,助你写出更高效的Python代码。
218 0

推荐镜像

更多