关于Python,我们前面的基础部分,基本也说完了,包括我们也讲了高阶特性,面向对象编程。现在我来补充一个知识:命名空间和作用域。
这是Python两个重要的概念,它们决定了变量的可见性和访问范围。理解命名空间和作用域可以帮助我们更好地编写代码,避免命名冲突,并提高代码的健壮性。
1.简介
首先,命名空间(Namespaces)就像是一个大字典,里面存着变量名和对应的东西(比如值或者函数)。这个字典告诉程序:“哥们,当我说这个名字的时候,我指的是这个东西!”但有趣的是,在不同的地方(也就是不同的命名空间)里,同一个名字可能会指向不同的东西。就像同名字的人在不同的城市,但是他们却是两个不一样的人!
然后,作用域(Scopes)就是规定了你在哪里能找到这些变量或者修改它们的值。它有点像是规定了一个变量的“活动范围”。在Python中,作用域告诉你:“哥们,这个地方,你可以用这个名字,改它的值也没问题!但那个地方,这个名字可就不认识了!”
所以,命名空间告诉你这个名字指的是啥,而作用域告诉你这个名字在哪里能用。
记住这两个概念,你就能理解Python程序中的变量是如何工作!
为什么命名空间和作用域对Python程序员重要?
- 避免命名冲突: 使用命名空间可以避免不同模块或库中的相同名称的变量或函数产生冲突。
- 代码组织和结构化: 通过命名空间,可以更好地组织代码,使代码结构更加清晰。
- 作用域规则: 了解作用域规则可以帮助程序员理解变量的生命周期和可见性,从而编写更加健壮的代码。
2. 命名空间(Namespaces)
命名空间(Namespaces)是一个从名称到对象的映射,它允许程序员在代码中避免名称冲突。Python解释器会在多个不同的命名空间中管理这些名称。这些有助于确定变量的范围和可见性。
不同类型的命名空间
- 内置命名空间(Built-in Namespace):
- 包含了 Python 解释器启动时就加载到内存中的名称,这些名称对应着 Python 的内置函数和异常等。
- 例如,
print()
、len()
等函数都属于内置命名空间。
- 全局命名空间(Global Namespace):
- 全局命名空间是指在模块级别定义的名称空间,一般是指在函数外部定义的变量和函数名。
- 在一个模块中,全局命名空间始终有效,模块的全局变量和函数在整个模块中可见。
- 局部命名空间(Local Namespace):
- 每个函数调用会创建一个局部命名空间,用于存储该函数内部定义的变量名。
- 这些变量在函数执行期间有效,函数执行结束后,局部命名空间被销毁。
- 在函数内部定义的变量、函数参数以及由函数调用所创建的局部变量,都属于局部命名空间。
如下图:
示例代码:
# 全局命名空间
global_var = "I'm in the global namespace"
def example_function(arg1):
# 局部命名空间
local_var = "I'm in the local namespace"
print(arg1)
# 内置命名空间
print(len("hello")) # 内置函数 len() 属于内置命名空间
在这里:
global_var
属于全局命名空间,example_function
和local_var
属于example_function
的局部命名空间。函数len()
属于内置命名空间。
3. 作用域(Scopes)
作用域(Scopes)是在程序中定义变量的区域,它决定了变量的可见性和访问规则。作用域规则确定了在何处可以访问变量以及如何解析变量名。
Python 的作用域规则按照以下顺序来查找变量:
1.局部作用域(Local Scope):
-
- 定义在函数内部的变量属于局部作用域。
- 在函数内部声明的变量只在该函数内部可见,函数执行结束后这些变量的作用域也就结束了。
2.嵌套作用域(Enclosing Scope):
-
- 嵌套作用域是指在函数内部定义的内部函数的作用域。
- 内部函数可以访问外部函数中的变量,但不能修改它们。
3.全局作用域(Global Scope):
-
- 全局作用域是指在模块级别定义的变量的作用域。
- 在模块中定义的变量可以在该模块中的任何地方使用。
4.内置作用域(Built-in Scope):
-
- 内置作用域包含了 Python 的内置函数和异常等。
- 这些名称在任何地方都可以访问到,无需导入任何模块。
上面这张图讲了作用域的四大规则,就是 LEGB 规则。下面接着看:
4. LEGB 规则
LEGB 规则的含义
LEGB 规则描述了 Python 中变量名解析的顺序,它按照如下顺序查找变量:
- L - Local(局部作用域)
- E - Enclosing(嵌套作用域)
- G - Global(全局作用域)
- B - Built-in(内置作用域)
当Python 在运行时遇到一个变量引用时,就会按照 LEGB 规则从局部作用域开始查找变量,然后逐级向上查找直到找到对应的变量或者查找完所有作用域。
LEGB 中的每个部分
作用域 | 描述 |
Local | - 最里层的作用域,在函数内部定义的变量属于局部作用域。 |
- 函数参数也属于局部作用域。 | |
Enclosing | - 包含了非全局(non-global)的嵌套函数的作用域。 |
- 在一个函数内部定义另一个函数,内部函数可以访问外部函数中的变量。 | |
Global | - 包含了模块级别定义的变量的作用域。 |
- 在模块中定义的变量可以在该模块中的任何地方使用。 | |
Built-in | - 包含了 Python 的内置函数和异常等。 |
- 这些名称在任何地方都可以访问到,无需导入任何模块。 |
简单来看一个示例代码:
x = 'global'
def outer():
x = 'outer'
def inner():
x = 'inner'
print(x) # 首先在局部作用域查找,输出 'inner'
inner()
outer() # 输出 'inner',因为 inner() 函数内部的 x 是局部变量
print(x) # 输出 'global',因为这里访问的是全局变量 x
在这里:inner()
函数首先在自己的局部作用域中查找变量 x
,找到了 'inner'
。而在最后一个 print(x)
中,因为没有在函数内部重新定义 x
,所以找到的是全局变量 x
,输出 'global'
。
5. 示例和应用
老规矩,通过几个示例和应用,讲一讲。
示例 1:不同作用域下变量的可见性
# 全局作用域
global_var = "I'm in the global scope"
def func():
# 局部作用域
local_var = "I'm in the local scope"
print(global_var) # 可以访问全局变量
print(local_var) # 可以直接访问局部变量
func()
print(global_var) # 可以在函数外部访问全局变量
# print(local_var) # 这里会报错,因为无法直接访问局部变量
示例 2:嵌套作用域
def outer():
outer_var = "I'm in the outer function"
def inner():
inner_var = "I'm in the inner function"
print(inner_var) # 可以直接访问内部函数的变量
print(outer_var) # 可以访问外部函数的变量
inner()
# print(outer_var) # 这里会报错,因为无法直接访问外部函数的变量
outer()
6. 常见问题与注意事项
在这里,我通过两个常见的问题,来看看一些注意事项。
- 未声明全局变量就在函数内修改:
x = 10
def func():
x += 5 # 会报错,因为 x 没有在函数内部声明为 global
func()
这里局部变量冲突, 解决方法是在函数内部使用 global
关键字声明变量 x
为全局变量。
- 在嵌套函数中修改外部函数变量:
def outer():
count = 0
def inner():
count += 1 # 会报错,因为 count 没有在内部函数中声明为 nonlocal
inner()
print(count)
outer()
这里的问题也一样,解决方法为:在内部函数中使用 nonlocal
关键字声明变量 count
为非局部变量。
总结
熟练掌握命名空间和作用域就是你成为 Python 高级程序员的基础,也是学习更复杂 Python 程序设计模式的必要前提。
下面是一些全局变量和局部变量的使用技巧:
技巧 | 描述 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
尽量避免全局变量 | 减少命名冲突和难以追踪的问题 | 代码更清晰易懂,维护性更高 | 可能需要更多参数传递 | 代码中变量较少,逻辑相对简单 |
传递参数 | 通过函数参数传递值 | 代码更清晰,易于测试 | 参数过多可能导致代码难以阅读 | 函数需要多个数据进行操作 |
使用常量 | 定义不会改变的变量为常量 | 提高代码可读性和可维护性 | 可能导致代码不够灵活 | 变量值固定,不会发生变化 |
如何避免命名冲突:
技巧 | 描述 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
命名规范 | 使用有意义的变量名,遵循命名规范 | 降低命名冲突的可能性 | 需要一定的时间和精力成本 | 所有代码 |
模块化代码 | 将代码模块化,使用模块和包 | 将变量隔离在不同的命名空间中 | 代码结构可能变得更加复杂 | 代码量较大,逻辑较为复杂 |
关于命名空间和作用域,我们就讲这么多,有机会再一起讨论。
欢迎大家和我一起继续学习、记录python的下一个知识点。
如果感觉阅读对您还有些作用,可以评论留言,关注我。谢谢您的阅读!
往期学习:
Python安装教程(版本3.8.10)windows10
Linux系统:安装Conda(miniconda)
Conda快速安装的解决方法(Mamba安装)
VSCode安装教程(版本:1.87.0)Windows10
Python基础语法:从入门到精通的必备指南
Python的基本数据类型
Python数据类型间的转换(隐式、显式)-CSDN博客
Python基础知识:运算符详解-CSDN博客
Python基础知识:数字类型及数学函数详解-CSDN博客
Python字符串操作及方法详解!一篇就搞定!-CSDN博客
Python列表及其操作详解,从此不再迷茫!-CSDN博客
Python元组(Tuple)深度解析!
Python字典的使用技巧(一篇详解)
Python条件控制深度解析,成为编程必备
Python循环语句全解析(附实战演练)
Python函数高效编程技巧,提升你的代码效率!
Python模块和包全解析,一篇文章就够!
Python lambda(匿名函数),一文详解
Python面向对象编程:合集篇(类、对象、封装、继承和多态)