Python高级之名称空间与作用域

发布时间 2023-12-12 16:36:58作者: Lea4ning

名称空间与作用域

【一】名称空间

  • 名称空间是一个存储变量名称到对象的映射的结构,它允许你在程序中访问和操作变量。在 Python 中,名称空间可以分为以下几种:
    1. 内置名称空间(Built-in Namespace):
      • 包含内置的函数和异常名称,如 print()len()ValueError 等。这个名称空间在 Python 解释器启动时创建,通常不会改变。
    2. 全局名称空间(Global Namespace):
      • 包含在模块级别定义的变量和函数。全局名称空间在整个程序运行过程中都是可见的。
    3. 局部名称空间(Local Namespace):
      • 包含在函数内部定义的变量和函数。局部名称空间仅在函数被调用时存在,函数执行结束后销毁。
    4. 类名称空间(Class Namespace):
      • 包含在类定义中定义的类变量和方法。类名称空间在类被定义时创建,实例化类时,每个实例将有自己的实例名称空间。
  • 名称空间的加载顺序
    • 内置名称空间->全局名称空间->局部名称空间,
    • 而查找一个名字,必须从三个名称空间之一找到,查找顺序为:
    • 局部名称空间->全局名称空间->内置名称空间。
print('这是内置名称空间')   # print()作为Python解释器自带的函数,随Python解释器的打开而打开
name = '这是全局名称空间'   # name在整个代码文件中,都可以取值
def add():
    x = '这是局部名称空间'   # 只有在add()函数内部时才可以调用到x
class 巴拉巴拉   # 还没学到类,想起来补了就补一下

【二】作用域

【1】作用域的4种类型(E嵌套L局部G全局B内置)

  • 作用域定义了一个变量在程序中的可见范围,即变量可以在哪里被访问。在 Python 中,主要有以下几种作用域:
    1. 嵌套作用域(Enclosed Scope)
      • 一般是在函数中嵌套函数的时候,外层函数的变量作用域
    2. 局部作用域(Local Scope):
      • 在函数内部定义的变量具有局部作用域,只能在函数内部访问。
    3. 全局作用域(Global Scope):
      • 在模块级别定义的变量具有全局作用域,可以在整个模块中访问。
    4. 内置作用域(Built-in Scope):
      • 内置的名称空间中定义的变量和函数具有内置作用域,可以在整个程序中访问。
print()   # 内置作用域   # 可以在整个程序中访问
name = "name 的作用域就是全局作用域"   # 可以在整个模块中访问
def index():
    a = 1
    # a的作用域就是局部作用域   # 只能在函数内部访问
    def inner():
        b = 2
        # b的作用域就是嵌套作用域  # 只能在内层函数内访问
        
# 函数内部的变量名在外部是不可以访问调用到的,如果需要访问,需要return将值返回出去

【2】ELGB规则

  • ELEB规则:按照E--->L--->G--->B的顺序查找变量。
    • 也就是,当不同命名空间具有相同变量名称的变量时,我们首先查找局部变量,如果没有查到,再向全局变量查找。

(1)基于命名空间的常见变量类型

  • 局部变量:
    • 在一个函数体的内部定义的变量
    • 作用域为函数内部
    • 查看局部变量命令:locals()
  • 全局变量
    • 在函数体外部,文件最外层定义的变量
    • 作用域为整个文件内部
    • 查看全局变量命令:globals()
  • 注意:
    • 变量访问规则:从内到外
    • 全局变量和局部变量重名时,采用就近原则

(2)案例讲解

# 定义了一个全局变量G,并把"G全局"赋值给a
a = "G全局"


def myfunc():
    # 定义了一个局部变量a,并把"E局部1"赋值给a
    a = "E局部"

    def inner():
        # 定义了一个局部变量a,并把"L局部2"赋值给a
        a = "L局部2"
        print(f"inner打印的a:{a}")

    inner()
    print(f"myfunc打印的a:{a}")


myfunc()
print(f"__main__打印的a:{a}")
# inner打印的a:L局部2
# myfunc打印的a:E局部
# __main__打印的a:G全局
  • 结果如下

img

【3】变量的修改global nonlocal

1)global修改全局变量

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

    # 定义一个全局变量 a
    a = 1
    
    
    def fun1():
        # 修改全局变量
        a = a + 2
        print(a)
    
    
    fun1()
    print(a)
    
    # UnboundLocalError: local variable 'a' referenced before assignment
    

    img

  • 可以看到,当我们试图在函数fun1创建的局部作用域内改变全局变量a就会报错

    • 但如果在修改之前使用global关键字声明时,就会正常修改外部的全局变量a
# 定义一个全局变量 a
a = 1


def fun1():
    global a

    # 修改全局变量
    a = a + 2
    print(f"函数内部修改全局变量 : {a}")


fun1()
print(f"调用函数后全局变量 : {a}")
# 函数内部修改全局变量 : 3
# 调用函数后全局变量 : 3

img

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

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

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

    return fun2


temp = fun1()  # 调用fun1
temp()  # 调用fun2
# UnboundLocalError: local variable 'a' referenced before assignment

img

  • 可以看到,报错和在函数内不使用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

img

  • 可以看到是正常修改的