Python名称空间和作用域

发布时间 2023-12-11 16:11:38作者: Fredette

【一】名称空间与闭包

  • 名称空间即存放名字与对象映射/绑定关系的地方。
    • 对于x=3
    • Python会申请内存空间存放对象3,然后将名字x与3的绑定关系存放于名称空间中
    • del x表示清除该绑定关系。
  • 在程序执行期间最多会存在三种名称空间

【1】内建名称空间

  • 伴随python解释器的启动/关闭而产生/回收
    • 因而是第一个被加载的名称空间,用来存放一些内置的名字,比如内建函数名
print(msg)

【2】全局名称空间

  • 伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中,如下名字
import sys #模块名sys

x=1 #变量名x

if x == 1:
    y=2 #变量名y

def foo(x): #函数名foo
    y=1
    def bar():
        pass

Class Bar: #类名Bar
	pass

【3】局部名称空间

  • 伴随函数的调用/结束而临时产生/回收,函数的形参、函数内定义的名字都会被存放于该名称空间中
def foo(x):
    y=3 #调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部名称空间中

【4】名称空间的加载顺序是

  • 内置名称空间->全局名称空间->局部名称空间,
  • 而查找一个名字,必须从三个名称空间之一找到,查找顺序为:
  • 局部名称空间->全局名称空间->内置名称空间。

【二】作用域

【1】变量作用域

  • 变量的作用域
    • Python是静态作用域,也就是说Python中,变量的作用域源于它在代码中的位置
    • 在不同的位置,可能有不同的命名空间。命名空间是变量作用域的体现形式
  • python变量作用域一般有4种:
    • Local(局部变量)
    • Enclosed(嵌套)
    • Global(全局)
    • Built-in(内置)

(1)Local(局部变量)

  • Local(局部变量):暂时的存在,依赖于创建该局部作用域的函数。函数存,则局部变量存,函数亡,则局部变量亡。
  • 作用范围:当前整个函数体范围
def fun():
    # 只有在函数内部的变量
    b = 2
    print(b)  # 输出2


fun()
# 调用函数后,发现找不到变量 b 是因为在全局作用域中找不到 变量 b
print(b)  # 报错

'''
Traceback (most recent call last):
  File "E:\PycharmProjects\demo\venv\evelen day.py", line 10, in <module>
    print(b)  # 报错
NameError: name 'b' is not defined
'''

(2)Enclosed(嵌套)

  • Enclosed(嵌套):一般是在函数中嵌套函数的时候,外层函数的变量作用域。
  • 作用范围:闭包函数
# Enclosed(嵌套)作用域
def fun1():
    b = 2
    print("这是fun1打印的:", b)

    def fun2():
        # 函数 func1 里面 嵌套的 函数 func2 里面的作用域就是 嵌套作用域
        print("这是fun2打印的:", b)
    
    # 将内部函数 func2 的内存地址返回
    return fun2


# 调用 函数 fun1 , 函数 func1 的返回值是内部函数 fuc2的函数地址
temp = fun1()

# 调用 函数 fun1 的返回值(fuc2的函数地址) , 从而执行 函数 fuc2
temp()
# 这是fun1打印的: 2
# 这是fun2打印的: 2

(3)Global(全局)

  • Global(全局):一般模块文件顶层声明的变量具有全局作用域,从外部来看,模
    块的全局变量就是一个模块对象的属性,仅限于单个模块文件中。
  • 作用范围:当前模块(文件)
# Global(全局)作用域

# 定义在全局的变量,在本文件任意位置可调用该变量
a = 2


def fun1():
    print("这是fun1打印的:", a)


fun1()
print(a)

# 这是fun1打印的: 2
# 2

(4)Built-in(内置)

Built-in(内置):解释器内置的变量,比如int, str等。

作用范围:所有模块(文件)

Python中没有块级作用域。

块级作用域:代码块中的变量,比如if、while、for后面的代码

【3】变量的修改

(1)global修改全局变量

  • 一般全局变量一经定义后几乎是不用改的,也不允许在局部修改全局变量,除非使用Global关键字声明。
# 定义一个全局变量 a
a = 1


def fun1():
    # 修改全局变量
    a = a + 2
    print(a)


fun1()
print(a)

(2)nonlocal修改外层函数变量

  • 在函数中嵌套函数时,嵌套在里面的函数创建的作用域内一般也是不允许改变外层函数变量的值的
  • 除非是nonlocal关键字声明
  • 如下
# 不使用nonocal声明,修改外层函数变量值
def fun1():
    a = 1

    def fun2():
        a += 2
        print(a)

    return fun2


temp = fun1()  # 调用fun1
temp()  # 调用fun2
'''
Traceback (most recent call last):
  File "E:\PythonProjects\def_func.py", line 137, in <module>
    temp()  # 调用fun2
  File "E:\PythonProjects\def_func.py", line 130, in fun2
    a += 2
UnboundLocalError: local variable 'a' referenced before assignment
'''
  • 可以看到,报错和在函数内不使用global修改全局变量报的错是一样的
  • 当使用nonlocal声明后再修改就不会报错了
# 不使用nonocal声明,修改外层函数变量值
def fun1():
    a = 1
    print(f"我是func1的变量修改前:{a}")

    def fun2():
        nonlocal a  # 使用nonocal声明
        a += 2
        print(f"这是内层嵌套修改局部变量后 :>>>> {a}")  # 修改后

    print(f"我是func1的变量修改后:{a}")
    return fun2


temp = fun1()  # 调用fun1
temp()  # 调用fun2

# 我是func1的变量修改前:1
# 我是func1的变量修改后:1
# 这是内层嵌套修改局部变量后 :>>>> 3