Python系列(7)| 命名空间、作用域

垄山小站 / 2024-08-23 / 原文

 

1. 命名空间 (Namespace)

    Python 中的 命名空间 (Namespace) 和作用域是密切相关的概念。Python 命名空间 (Namespace) 可以视为一个字典,其中键是变量名,值是与之关联的对象。

    各个命名空间是独立的,同一个命名空间中不能有重名(重名的以后一个为准),不同的命名空间是可以重名。
    
    命名空间类似于电脑的文件系统,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。

    一般有三种命名空间:

        内置名称 (built-in names): Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等;
        全局名称 (global names): 模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量;
        局部名称 (local names): 函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。

    命名空间查找顺序: 局部的命名空间 -> 全局命名空间 -> 内置命名空间。

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

2. 作用域

    作用域就是 Python 程序可以直接访问命名空间的范围,或者说变量的有效范围。

    Python 的作用域有 4 种:

        L(Local):最内层,包含局部变量,比如一个函数/方法内部。
        E(Enclosing):包含了非局部 (non-local) 也非全局 (non-global) 的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
        G(Global):当前脚本的最外层,比如当前模块的全局变量。
        B(Built-in): 包含了内置的变量/关键字等。

    作用域查找遵循 LEGB 规则:
    
        Local -> Enclosing -> Global -> Built-in
    
    内置作用域是通过一个名为 builtins 的标准模块来实现的,查看内置作用域里的变量和关键字,可以运行如下代码:

        import builtins
        print(dir(builtins))

        注:可是使用 dir() 函数查看模块、类、对象等结构内部所包含的属性列表和方法列表。

    Python 中只有模块(module)、类(class)和函数(def、lambda)才会引入新的作用域,其它的代码块(比如 if/elif/else/、try/except、for/while 等)不会引入新的作用域。
    
    示例:
        #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        # 全局变量
        s = 'Global'
        def func():

            # nonlocal 变量
            s = 'Enclosing'

            def inner_func():
                # 局部变量
                s = 'Local'
                print(s)    # 输出局部变量 s
                
            inner_func()
            print(s)    # 输出 nonlocal 变量 s

        if __name__ == "__main__":
            func()
            print(s)    # 输出全局变量 s
            print(x)    # 输出未定义变量 x

    输出结果如下:
        Local
        Enclosing
        Global
        Traceback (most recent call last):
        File "d:/pythonDemo/func3.py", line 22, in <module>
            print(x)    # 输出未定义变量 x
        NameError: name 'x' is not defined


        注:在各命名空间都找不到变量 x, 抛出了一个 NameError 异常。
        
    在 fun() 作用域内,给 s 赋值 'Enclosing',没有改变全局变量 s 的值。在 inner_func() 作用域内,给 s 赋值 'Local',没有改变nonlocal 变量 s 的值。在作用域内,想要修改作用域外的变量,需要用到 global 或 nonlocal 关键字。

 

3. global 和 nonlocal 关键字

    在 Python 中,global 关键字用于标识变量是全局的。如果在函数内部要修改全局变量,需要使用 global 关键字。

    nonlocal 关键字是用于标识变量是外部的(非全局)。如果在函数内部要修改外部函数的变量,需要使用 nonlocal 关键字。

    示例1:

        #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        # 全局变量
        s = 'Global'
        def func():

            # nonlocal 变量
            s = 'Enclosing'

            def inner_func():
                # 标识 s 是全局变量
                global s
                s = 'Local'
                print(s)    # 输出全局变量 s
                
            inner_func()
            print(s)    # 输出 nonlocal 变量 s

        if __name__ == "__main__":
            func()
            print(s)    # 输出全局变量 s 


    输出结果如下:

        Local
        Enclosing
        Local

    示例2:

        #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        # 全局变量
        s = 'Global'
        def func():

            # nonlocal 变量
            s = 'Enclosing'

            def inner_func():
                # 标识 s 是 nonlocal 变量
                nonlocal s
                s = 'Local'
                print(s)    # 输出 nonlocal 变量 s
                
            inner_func()
            print(s)    # 输出 nonlocal 变量 s

        if __name__ == "__main__":
            func()
            print(s)    # 输出全局变量 s


    输出结果如下:

        Local
        Local
        Global


4. 获取作用域范围中的变量

    1) globals() 函数

        globals() 函数是 Python 的内置函数,在函数内外调用(包括全局作用域),都返回一个全局变量字典。

        示例:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            # 全局变量
            a = 1
            b = 2
            c = 3
            def func():
                # nonlocal 变量
                i = 11
                j = 12
                
                def inner_func():
                    # 局部变量
                    x = 101
                    y = 102
                    
                inner_func()

            if __name__ == "__main__":
                print(globals())


            输出结果如下:

                {... , 'a': 1, 'b': 2, 'c': 3, ...}

            注: globals() 函数返回的字典中,会默认包含有很多下划线变量,它们是 Python 主程序内置的属性和方法,不用管它们。

    2) locals() 函数

        locals() 函数也是 Python 的内置函数,在函数内外调用,返回一个当前作用域内所有变量的字典。

        示例:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            # 全局变量
            a = 1
            b = 2
            c = 3
            def func():
                # nonlocal 变量
                i = 11
                j = 12
                print(locals())
                
                def inner_func():
                    # 局部变量
                    x = 101
                    y = 102
                    print(locals())
                    
                inner_func()

            if __name__ == "__main__":
                func()
                print(locals())


        输出结果如下:

            {'i': 11, 'j': 12}
            {'x': 101, 'y': 102}
            {... , 'a': 1, 'b': 2, 'c': 3, ...}

        注: 在函数内部调用 locals() 函数,会获得包含所有局部变量的字典;在全局作用域调用,locals() 的功能和 globals() 函数相同。

    3) vars(object) 函数

        vars(object) 函数也是 Python 内置函数,其功能是返回一个指定 object 对象范围内所有变量组成的字典。

        示例:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            # 全局变量
            a = 1
            b = 2
            c = 3

            class Person:
                name = 'Python'
                age = 20

            if __name__ == "__main__":
                print(vars(Person))
                print(vars())


        输出结果如下:

            {..., 'name': 'Python', 'age': 20, ...}
            { ... 'a': 1, 'b': 2, 'c': 3, ...}

        注:如果不传入 object 参数,vars() 和 locals() 的作用完全相同。