Python命名空间作用域浅析

网友投稿 734 2022-05-30

Python3 命名空间和作用域

命名空间

先看看官方文档的一段话:

A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。

命名空间(Namespace)是从名称到对象的映射,像是一个dict,key是变量的名字,value是变量的值。大部分的命名空间都是通过 Python 字典来实现的

(__dict__)。

命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。

我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。

一般有三种命名空间:

内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。

全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。

局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

命名空间查找顺序:

假设我们要使用变量 runoob,则 Python 的查找顺序为:局部的命名空间去 -> 全局命名空间 -> 内置命名空间。

如果找不到变量 runoob,它将放弃查找并引发一个 NameError 异常:

NameError: name 'runoob' is not defined。

对于闭包来说,这里有一点区别,如果在local namespace中找不到变量的话,还会去父函数的local namespace中找变量。

命名空间的生命周期:

命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。

因此,我们无法从外部命名空间访问内部命名空间的对象。

我们可以使用locals() globals()   __dict__访问命名空间【locals()和globals()有一个区别是,locals只读,globals可以写】

import sys m =sys.modules[__name__] #sys.modules获取了当前运行的模块 def f(x=1,y=2): m=3 print(locals()) #获取当前的命名空间(函数体内即为函数内部的命名空间) return 0 f() print(m.__dict__) #__dict__成员获取了当前模块的命名空间 print(locals()) #获取当前的命名空间(全局) print(globals()) #获取全局的命名空间

打印结果

{'x': 1, 'y': 2, 'm': 3} {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A2B0FA4188>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/flask-vuejs-madblog-master/study/100day/1.py', '__cached__': None, 'time': , 'datetime': , 'sys': , 'm': , 'f': } {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A2B0FA4188>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/flask-vuejs-madblog-master/study/100day/1.py', '__cached__': None, 'time': , 'datetime': , 'sys': , 'm': , 'f': } {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A2B0FA4188>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/flask-vuejs-madblog-master/study/100day/1.py', '__cached__': None, 'time': , 'datetime': , 'sys': , 'm': , 'f': }

【解释】:遇到一个名字的时候,Python解释器首先会去本地命名空间(Local)中查找,然后再去其所在函数的作用域(Enclosing Function)中查找,如果还没找到,就去全局命名空间(Global)中查找,最后会去__builtin__这个模块中查找,__builtin__模块在 Python3 中已经重命名成了builtins

【附】

from module import 和 import module

使用import module时,module本身被引入,但是保存它原有的命名空间,所以我们需要使用module.name这种方式访问它的 函数和变量。

from module import这种方式,是将其它模块的函数或者变量引到当前的命名空间中,所以就不需要使用module.name这种方式访问其它的模块的方法了。

if __name__ trick

python中的module也是对象,所有的modules都有一个内置的属性__name__,模块的__name__属性的值取决于如何使用这个模块,如果import module,那么__name__属性的值是模块的名字。如果直接执行这个模块的话,那么__name__属性的值就是默认值__main__。

module的一些内置属性

__name__: 上面已经介绍过

__file__ : 当前module的绝对路径

__dict__:

Python命名空间和作用域浅析

__doc__ :

__package__:

__path__:

作用域

A scope is a textual region of a Python program where a namespace is directly accessible. "Directly accessible" here means that an unqualified reference to a name attempts to find the name in the namespace.

作用域就是一个 Python 程序可以直接访问命名空间的正文区域。

在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。

Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:

有四种作用域:

L(Local):最内层,包含局部变量,比如一个函数/方法内部。

E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。

G(Global):当前脚本的最外层,比如当前模块的全局变量。

B(Built-in): 包含了内建的变量/关键字等。,最后被搜索

规则顺序: L –> E –> G –>gt; B。

在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。

实战分析:

x = 1234 def test(): print(x) x = 'abc' test()

在上面的函数中,我们要打印一个名字x,它首先会去本地命名空间中查找,没有找到。然后去当前函数test的作用域中查找,找到了。此时Python解释器就会发现x这个名字还没有添加到local本地命名空间中,就被引用了。所以就会抛出一个异常,说x还未被赋值就被引用了。

import dis x = 1234 def test_right(): print(x) dis.dis(test_right) print('-' * 20) x = 1234 def test_error(): print(x) x = 'abc' dis.dis(test_error)

5 0 LOAD_GLOBAL 0 (print) 3 LOAD_GLOBAL 1 (x) 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE -------------------- 13 0 LOAD_GLOBAL 0 (print) 3 LOAD_FAST 0 (x) 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 14 10 LOAD_CONST 1 ('abc') 13 STORE_FAST 0 (x) 16 LOAD_CONST 0 (None) 19 RETURN_VALUE

通过查看这两个函数的反汇编出来的代码可以看到,这两个函数访问x的时候,一个是通过LOAD_GLOBAL指令,访问的全局命名空间,另外一个则是通过LOAD_FAST指令访问的本地命名空间。

Python

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:excel表格数据公式不生效如何解决
下一篇:2021年大数据Spark(二十七):SparkSQL案例一花式查询和案例二WordCount
相关文章