第二十七篇 名称空间与作用域

第二十七篇 名称空间与作用域

函数内部的函数只能在函数内部调用,不能再函数外部调用,要想知道为什么,我们就需要了解名称空间和作用域

一、名称空间

名称空间(name spaces):在内存管理那一章节中,我们知道变量的创建其实就是在内存开辟了一个新的空间。但是变量名的存储之前一直没有提及,其实在内存中有一块内存存储着变量名与变量之间绑定关系的空间,这个空间叫做名称空间

1.内置名称空间

  • 1.内置名称空间:存放python解释器自带的名字,比如 int、float、len、strip
  • 2.生命周期:在python解释器启动时生效,在python解释器关闭时失效

2.全局名称空间

  • 1.定义:除了内置和局部的名字之外,其余都放在全局名称空间。如下面的a、f、l、i
  • 2.生命周期:在文件执行时生效,在文件执行结束后失效
a = 0

def f():
    pass

l = [2,'k']

if 2>1:
    if 0<1:
        i = 6

3.局部名称空间

  • 1.定义:用于存放函数调用期间函数体产生的名字。比如下面的f2
  • 2.生命周期:在文件执行时函数调用期间时生效,在函数执行结束后失效
def f():
    def f2():
        print('ha')
    f2
    
f()

4.加载顺序

  • 由于.py文件是由python解释器打开的,因此一定是在python解释器中的内置名称空间加载结束后,文件才开始打开,这个时候才会产生全局名称空间,但文件内有某一个函数被调用的时候,才会开始产生局部名称空间,因此名称空间的加载顺序或者说执行顺序为:内置名称空间---->全局名称空间---->局部名称空间

5.查找顺序

  • 由于名称空间是用来存放变量名与值之间的绑定关系的,所以但凡要查找名字,就必须从名称空间中的三者之一找,查找顺序是,从当前的所在位置开始查找,如果当前所在的位置为局部名称空间,则查找顺序为:局部名称空间---->全局名称空局---->内置名称空间(也就是从当前位置开始,只会往外找)

  • 虽然有些名字属于全局名称空间或局部名称空间,但它总归是一个内存空间,在执行完程序或调用完函数之后,它的名称空间也就会被释放掉,而与它所对应的值的内存空间由于引用计数为0,也会被垃圾回收机制所回收

    x = 0
    y = 1
    
    def f():
        x = 66
        y = 233
        print(x,y)
        
    f()   # 66 233
    
x = 0

def f():
    print(x)
    
x = 6
f()    #6

二、作用域

1.须知

1.作用域是python的一块文本区域,或者说是一块代码区域。这个区域中有很多变量、函数,或者说是对象,在这块区域,这些对象的名称空间可以被访问

2.名字作用域就是名称空间所能影响到的代码区域

3.作用域只是代码区域,属于静态的,名称空间是随着解释器运行而产生的,是动态的

4.在静态的作用域中访问动态的名称空间,也会造成作用域近似于具有了动态性。也即是说会存在一个这样的作用域,多个名称空间可以影响它

x = 6
def f():
    print(x)
f()      # 6
x = 88
f()      # 88
  • 作用域就是作用的区域

  • 只需要记住:全局变量和局部变量,可能名字一样,但是两者根本不是同一种

2.名字查找规则:LEGB规则

在程序运行时,至少存在三个名称空间可以被直接访问的作用域:

1. local :包含局部名字的最内层(innermost)作用域,如函数/方法/类的内部局部作用域

2.enclosing:根据嵌套层次从内到外搜索,包含非局部(nonlocal)非全局(nonglobal)名字的任意封闭函数的作用域。如两个嵌套的函数,内层函数的作用域是局部作用域,外层函数作用域就是内层函数的 enclosing 作用域

3.global:包含当前模块全局名字的作用域

4.built-in:包含内置名字的最外层作用域

5.名字作用域的访问顺序是:local ---> enclosing ---> global ---> built-in

def f():
    x = 2
f()
print(x)  # NameError
'''因为该变量x的名称空间属于局部名称空间,按照LEGB规则,在全局作用域中自然访问不到局部作用域,而且变量x所在的名称空间在函数执行完之后就被销毁了'''

3.全局作用域

  • 全局作用域:全局有效,全局存活,包含内置名称空间和全局名称空间
x = 6

def f():
    print(x)
    
f()    # 6

4.局部作用域

  • 局部作用域:局部有效,临时存储,只包含局部名称空间
def f1():
    def f2():
        def f3():
            print(x)
        x = 1
        f3()
    f2()
    
f1()      # 1

5.注意点

  • 作用域关系在函数定义阶段就固定死了,与函数的调用无关
x = 1

def f():   # 函数定义阶段
    print(x)   # 由于f()函数的局部作用域没有x,所以会先去全局找,此时x=1
    
def f2():
    x = 6   # 这个x只能在f2()函数的局部作用域起作用,对f()函数没有丝毫影响
    f()   
    
f2()    # 1

6.函数对象与作用域应用

def f():
    def f1():
        print('hah')
    return f1

a = f()   

def f2():
    a()
    
f2()      # hah

三、补充知识点(不推荐使用)

1.global关键字

  • 作用:修改全局作用域中的变量
x = 1

def f():
    x = 6
    def f2():
        x = 233
    f2()
    
f()
print(x)    # 在当前位置查找,所以是全局作用域的x=1
x = 1

def f():
    x = 6
    def f2():
        global x    # 可以将局部变量变为全局变量
        x = 233
    f2()
    
f()
print(x)    # 233

2.nonlocal关键字

  • 修改局部作用域中的变量
x = 1

def f():
    x = 6
    def f2():
        x = 233
    f2()
    print(x)
    
f()      # 6
x = 1

def f():
    x = 6
    def f2():
        nonlocal x     # 可以使内部函数变量在外部也能使用,但不能修改为全局变量
        x = 233
    f2()
    print(x)
    
f()      # 233

3.注意点

1.在局部想要修改全局的可变类型,不需要任何声明,可以直接修改

2.在局部想要修改全局的不可变类型,需要借助global声明,声明为全局变量

lis = []

def f():
    lis.append(1)

# 函数调用前
print(lis)    # []

# 函数调用后
f()
print(lis)     # [1]
原文地址:https://www.cnblogs.com/itboy-newking/p/10953501.html