四个函数搞清 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天前
|
数据挖掘 数据处理 索引
python常用pandas函数nlargest / nsmallest及其手动实现
python常用pandas函数nlargest / nsmallest及其手动实现
18 0
|
15天前
|
Python
python函数的参数学习
学习Python函数参数涉及五个方面:1) 位置参数按顺序传递,如`func(1, 2, 3)`;2) 关键字参数通过名称传值,如`func(a=1, b=2, c=3)`;3) 默认参数设定默认值,如`func(a, b, c=0)`;4) 可变参数用*和**接收任意数量的位置和关键字参数,如`func(1, 2, 3, a=4, b=5, c=6)`;5) 参数组合结合不同类型的参数,如`func(1, 2, 3, a=4, b=5, c=6)`。
14 1
|
7天前
|
Serverless 开发者 Python
《Python 简易速速上手小册》第3章:Python 的函数和模块(2024 最新版)
《Python 简易速速上手小册》第3章:Python 的函数和模块(2024 最新版)
40 1
|
8天前
|
索引 Python
Python高维变量选择:SCAD平滑剪切绝对偏差惩罚、Lasso惩罚函数比较
Python高维变量选择:SCAD平滑剪切绝对偏差惩罚、Lasso惩罚函数比较
10 0
|
9天前
|
Python
python学习-函数模块,数据结构,字符串和列表(下)
python学习-函数模块,数据结构,字符串和列表
49 0
|
9天前
05-python之函数-函数的定义/函数的参数/函数返回值/函数说明文档/函数的嵌套使用/函数变量的作用域
05-python之函数-函数的定义/函数的参数/函数返回值/函数说明文档/函数的嵌套使用/函数变量的作用域
|
11天前
|
Python
python学习10-函数
python学习10-函数
|
11天前
|
Python
python学习4-内置函数range()、循环结构、循环控制语句、else语句、嵌套循环
python学习4-内置函数range()、循环结构、循环控制语句、else语句、嵌套循环
|
14天前
|
测试技术 开发者 Python
Python中的装饰器:优雅而强大的函数修饰工具
在Python编程中,装饰器是一种强大的工具,用于修改函数或方法的行为。本文将深入探讨Python中装饰器的概念、用法和实际应用,以及如何利用装饰器实现代码的优雅和高效。
|
19天前
|
Python
Python函数学习应用案例详解
【4月更文挑战第7天】学习Python函数的应用,包括计算两数之和、判断偶数、计算阶乘、生成斐波那契数列及反转字符串。示例代码展示了函数接收参数和返回结果的功能,如`add(a, b)`求和,`is_even(num)`判断偶数,`factorial(n)`计算阶乘,`fibonacci(n)`生成斐波那契数,以及`reverse_string(s)`反转字符串。
14 1