Python的命名空间是Python程序猿必须了解的内容,对Python命名空间的学习,将使我们在本质上掌握一些Python中的琐碎的规则。
接下来我将分四部分揭示Python命名空间的本质:一、命名空间的定义;二、命名空间的查找顺序;三、命名空间的生命周期;四、通过locals()和globals() BIF访问命名空间
重点是第四部分,我们将在此部分观察命名空间的内容。
一、命名空间
Python使用叫做命名空间的东西来记录变量的轨迹。命名空间是一个 字典(dictionary) ,它的键就是变量名,它的值就是那些变量的值。
A namespace is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries。
在一个 Python 程序中的任何一个地方,都存在几个可用的命名空间。
1、每个函数都有着自已的命名空间,叫做局部命名空间,它记录了函数的变量,包括函数的参数和局部定义的变量。
2、每个模块拥有它自已的命名空间,叫做全局命名空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
3、还有就是内置命名空间,任何模块均可访问它,它存放着内置的函数和异常。
二、命名空间查找顺序
当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:
1、局部命名空间:特指当前函数或类的方法。如果函数定义了一个局部变量 x,或一个参数 x,Python 将使用它,然后停止搜索。
2、全局命名空间:特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。
3、内置命名空间:对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。
4、如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name 'aa' is not defined。
嵌套函数的情况:
1、先在当前 (嵌套的或 lambda) 函数的命名空间中搜索
2、然后是在父函数的命名空间中搜索
3、接着是模块命名空间中搜索
4、最后在内置命名空间中搜索
示例:
1 info = "Adress : " 2 def func_father(country): 3 def func_son(area): 4 city= "Shanghai " #此处的city变量,覆盖了父函数的city变量 5 print(info + country + city + area) 6 city = " Beijing " 7 #调用内部函数 8 func_son("ChaoYang "); 9 10 func_father("China ")
输出:Adress : China Shanghai ChaoYang
以上示例中,info在全局命名空间中,country在父函数的命名空间中,city、area在自己函数的命名空间中
三、命名空间的生命周期
不同的命名空间在不同的时刻创建,有不同的生存期。
1、内置命名空间在 Python 解释器启动时创建,会一直保留,不被删除。
2、模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。
3、当函数被调用时创建一个局部命名空间,当函数返回结果 或 抛出异常时,被删除。每一个递归调用的函数都拥有自己的命名空间。
Python 的一个特别之处在于其赋值操作总是在最里层的作用域。赋值不会复制数据——只是将命名绑定到对象。删除也是如此:"del y" 只是从局部作用域的命名空间中删除命名 y 。事实上,所有引入新命名的操作都作用于局部作用域。
示例:
i=1 def func2(): i=i+1 func2(); #错误:UnboundLocalError: local variable 'i' referenced before assignment
由于创建命名空间时,python会检查代码并填充局部命名空间。在python运行那行代码之前,就发现了对i的赋值,并把它添加到局部命名空间中。当函数执行时,python解释器认为i在局部命名空间中但没有值,所以会产生错误。
def func3(): y=123 del y print(y) func3() #错误:UnboundLocalError: local variable 'y' referenced before assignment #去掉"del y"语句后,运行正常
四、命名空间的访问
1、局部命名空间可以 locals() BIF来访问。
locals 返回一个名字/值对的 dictionary。这个 dictionary 的键是字符串形式的变量名字,dictionary 的值是变量的实际值。
示例:
def func1(i, str ): x = 12345 print(locals()) func1(1 , "first")
输出:{'str': 'first', 'x': 12345, 'i': 1}
2、全局 (模块级别)命名空间可以通过 globals() BIF来访问。
示例:
'''Created on 2013-5-26''' import copy from copy import deepcopy gstr = "global string" def func1(i, info): x = 12345 print(locals()) func1(1 , "first") if __name__ == "__main__": print("the current scope's global variables:") dictionary=globals() print(dictionary)
输出:(我自己给人为的换行、更换了顺序,加颜色的语句下面重点说明)
{
'__name__': '__main__',
'__doc__': 'Created on 2013-5-26',
'__package__': None,
'__cached__': None,
'__file__': 'E:\\WorkspaceP\\Test1\\src\\base\\test1.py',
'__loader__': <_frozen_importlib.SourceFileLoader object at 0x01C702D0>,
'copy': <module 'copy' from 'D:\\Python33\\lib\\copy.py'>,
'__builtins__': <module 'builtins' (built-in)>,
'gstr': 'global string',
'dictionary': {...},
'func1': <function func1 at 0x01C6C540>,
'deepcopy': <function deepcopy at 0x01DB28A0>
}
总结
1、模块的名字空间不仅仅包含模块级的变量和常量,还包括所有在模块中定义的函数和类。除此以外,它还包括了任何被导入到模块中的东西。
2、我们看到,内置命名也同样被包含在一个模块中,它被称作 __builtin__。
3、回想一下 from module import 和 import module 之间的不同。
使用 import module,模块自身被导入,但是它保持着自已的名字空间,这就是为什么您需要使用模块名来访问它的函数或属性:module.function 的原因。
但是使用 from module import function,实际上是从另一个模块中将指定的函数和属性导入到您自己的名字空间,这就是为什么您可以直接访问它们却不需要引用它们所来源的模块。使用 globals 函数,您会真切地看到这一切的发生,见上面的红色输出语句。
3、 locals 与 globals 之间的一个重要的区别
locals 是只读的,globals 不是
示例:
def func1(i, info): x = 12345 print(locals()) locals()["x"]= 6789 print("x=",x) y=54321 func1(1 , "first") globals()["y"]= 9876 print( "y=",y)
输出:
{'i': 1, 'x': 12345, 'info': 'first'}
x= 12345
y= 9876
解释:
locals 实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行改变对局部名字空间中的变量值并无影响。
globals 返回实际的全局名字空间,而不是一个拷贝。所以对 globals 所返回的 dictionary 的任何的改动都会直接影响到全局变量。
相关推荐
### Python命名空间的本质和加载顺序 #### 一、命名空间的定义 在Python中,**命名空间**是一种机制,用于存储变量与它们所关联的对象之间的映射关系。它本质上就是一个字典,其中键是变量名,值是这些变量对应的...
4. 模块导入与命名空间:了解import语句、from...import语句的使用,以及PYTHONPATH变量、命名空间和作用域的概念,对于编写模块化的代码非常重要。 5. 包与模块发布:理解如何在Python中组织代码成包,以及如何...
如果想要一次性导入模块的所有内容,可以使用`from 模块名 import *`语句,但这并不总是推荐的做法,因为它可能导致命名空间的污染,使代码可读性下降。相比之下,更常见的做法是使用`import 模块名`,然后通过`模块...
- **避免命名冲突**:在一个大型项目中,多个文件可能会定义相同的函数或变量名称,模块可以确保每个模块内的命名空间独立,避免了全局命名冲突。 - **代码重用**:模块内的函数和类可以在多个地方被导入和使用,...
Python模块是组织代码的一种方式,本质是.py文件,允许我们将代码划分为独立的单元,便于重用和管理。 在导入模块时,可以使用`import`关键字引入模块,如果要导入模块的所有内容,可以使用`from module_name ...
- **命名空间(Namespace)**是指在Python中存储所有变量名称的地方。每个作用域都拥有自己的命名空间,其中包含变量名及其对应的值。 #### 13. Python中的lambda是什么? - **Lambda**是一种匿名函数,用于定义简单...
包是一种管理Python模块命名空间的形式,本质上是一个包含__init__.py文件的目录。 #### 异常处理 异常处理允许程序在遇到错误时可以优雅地处理,而不是直接崩溃。Python提供了try-except-else-finally语句,用于...
变量的声明本质上是创建一个命名空间(namespace),其中存储了变量的名称和对应的值。变量的作用域决定了变量在程序中的有效范围。Python的作用域分为局部作用域和全局作用域,全局变量在程序的任何地方都有效,而...
- 这种方式会将模块中的所有非私有成员导入到当前命名空间。 ##### 二、判断题解析 1. **相比C++程序,Python程序的代码更加简洁、语法更加优美,但效率较低。(×)** - 虽然Python的代码通常更为简洁且易于...
- **全局变量**:在函数外或全局空间中声明的变量,可以被程序中的任何函数访问。这些变量在整个程序的作用域内都是可用的。 示例: ```python global_var = 10 def func(): print(global_var) # 输出 10 ...
4. **命名空间(Namespace)**:存储变量名和它们对应值的容器,可以理解为变量的作用域。 Python遵循LEGB规则(Local → Enclosing → Global → Built-in),当查找变量时,会依次搜索这四个作用域。 例如: ``...
虽然匿名函数不能访问外部作用域的变量,但它有自己的独立命名空间。其基本语法是`lambda 参数: 表达式`。例如,使用`lambda`可以快速定义一个简单的加法函数`add = lambda x, y: x + y`,其功能与普通的函数定义`...
Python采用的是“命名参数传递”,而非像C++那样的值传递或引用传递。在Python中,所有的变量本质上都是对象的引用。这意味着当你传递一个变量给函数时,实际上是传递了该对象的引用。不可变对象如字符串、元组和...
- 视频本质上是连续的图像帧序列。每个帧都是一个单独的图像,通过快速播放这些帧,人眼会感知到连续的动作。 - 要将视频切分为图片,我们需要逐帧读取视频,然后将每一帧保存为单独的图片文件。 3. **实现步骤**...
7. **文件路径和命名**:在处理大量图片时,文件路径和命名策略很重要。通常,开发者会根据视频名称和帧号创建动态文件名,例如"video_name_frame_number.jpg",确保每个帧都有唯一的标识。 8. **性能优化**:对于...
- 使用`from module_name import element`时,Python会将`element`直接导入到当前命名空间,使得可以直接访问而无需通过`module_name.element`的形式。 3. **导入包** - 导入包实际上是执行该包下的`__init__.py`...
可以使用`locals()`和`globals()`函数查看当前的局部和全局命名空间。 3. 变量解析规则(LEGB规则) Python遵循LEGB规则(Local, Enclosing, Global, Built-in)解析变量: - L(Local):函数内部定义的变量。 - E...
理解 Python 变量的本质对于优化代码和避免意外的副作用至关重要。例如,如果你希望创建一个新列表而不是引用原来的列表,你应该使用切片或复制操作,如 `c = a.copy()` 或 `c = a[:]`,这样 `c` 就会指向一个新的...