zoukankan      html  css  js  c++  java
  • 第二十七篇 名称空间与作用域

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

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

    一、名称空间

    名称空间(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]
    
  • 相关阅读:
    将表单赋予对对象
    sql server 锁
    设置SQL server服务器的dbo架构
    用ILSpy查看Session.SessionID的生成算法
    c#3.0新特性
    解决文件上传插件Uploadify在火狐浏览器下,Session丢失的问题
    VS2012 集成 IL DASM IL微软中间语言查看器
    认识
    操作符重载
    博客搬家了
  • 原文地址:https://www.cnblogs.com/itboy-newking/p/10953501.html
Copyright © 2011-2022 走看看