当前位置: 首页 > news >正文

python 之名称空间与作用域

python 之名称空间与作用域

引入

栈区 / 堆区

  • 栈区 : 存放的是变量名与变量值的内存地址映射关系

  • 堆区 : 存放的是值真正的位置

image-20201130195946393

一、名称空间

引入:

什么是名称空间?

  • 名称:定义的名字 空间:存放名字的地方

  • 名称空间即存放名字与对象映射/绑定关系的地方。

  • 名称空间只是虚拟的概念。栈区才是真正存在的。

名称空间有包含关系吗?

  • 名称空间之间本质是没有包含关系的,是互相独立的。

    image-20201130200748089

为什么要有名称空间?

  • 有了名称空间之后,就可以在栈区中存放相同的名字,让所有的名称不冲突。

查找名称空间的优先级是:

  • 在局部作用域查找名字时,起始位置是局部作用域,所以先查找局部名称空间,如果没有找到,再去全局作用域查找,还没有找到就在内置名称空间中查找,都没有找到就会抛出异常。

  • 查找顺序:局部名称空间>>全局名称空间>>内置名称空间

三种全称空间的个数:

  • 内置、全局1只有一个。局部可以多个。

 

二、名称空间的分类(三类)

1、内建名称空间(也叫内置名称空间)

  • 存放的名字:存放的是python解释器内置的名字

  • 存活周期:python解释器启动则产生,python解释器关闭则销毁。

示例:

#例如:我们常用的内置函数
>>> len
<built-in function len>
>>> print
<built-in function print>
>>> input
<built-in function input>

2、全局名称空间

  • 存放的是顶级名字(文件级别的名字)

  • 存活周期:在执行文件执行时生效,文件运行完毕或文件执行期间被删除则失效

    x = 1      #全局名称空间
    def foo(): #全局名称空间
        y = 2
    注意,"if"下面的定义的变量名都是全局的
    if 1 > 0        
        z = 3        #全局
        if 3 > 0: 
            p = 555  #全局

     

3、局部名称空间

  • 存放函数内定义的名字

  • 存活周期: 在调用函数时临时生效,函数调用完毕失效

示例
def f()
    x = 222    #局部名称空间
    
f()   #函数调用结束失效
​
示例二:
def func(a, b):
    pass
​
# 下面👇调用了4次func,虽然使用pass语句,但是在调用函数时,也会产生局部名称空间,且它们每调用一次,都会产生一个新的局部名称空间。
func(10, 1)
func(10, 1)
func(10, 1)
func(10, 1)

 

4、三种名称空间的之间的关系

三种名称空间的加载顺序:

  • 内置名称空间= > 全局名称空间= > 局部名称空间

三种名称空间中一定要有的名称空间:

  • 内置名称空间、全局名称空间

三种名称空间的销毁顺序:

  • 局部名称空间 =》 全局名称空间 =》 内置名称空间

三种名称空间的名字的查找优先级:在当前所在的位置,一层层向上查找

  • 如果当前在局部名称空间:

    • 局部名称空间 ==> 全局名称空间 ==> 内置名称空间

三种名称空间的名字的查找优先级示例

  1. 如果当前在局部名称空间:局部名称空间 => 全局名称空间 => 内置名称空间、

    input = 100
    def func():
        input = 10
        print(input)  # 10
    func()
    #ps: 取值顺序和加载顺序无关

     

  2. 如果当前在全局名称空间:全局名称空间 --> 内置名称空间

    • 示范一:

      input = 111 #1.在全局定义input = 111
      def func(): #2.定义f1函数
          input = 222 #4.在func局部名称空间内定义input = 222 
      func() #3.调用func,执行func函数体代码
      print(input) #5.在当前名称空间内取值,此时取值为111
      111
    • 示范二:

      x = 222 #
      def func():
          print(x) # 111
      x = 111  # 定义阶段print语句还没有执行,也就是说func中的名称空间还没有产生。当运行到这条代码,x与之前值222的内存地址的绑定关系解除。绑定了新的值111的内存地址。所以当调用func()时,func中的局部名称空间才产生,这个时候print在自己的名称空间中没有找到,跑到全局找到了x=111,所以上面返回111
      func()

       

    • 示范三(难点):名称空间的"嵌套"关系是以函数定义时为准的,与调用位置无关。

      x = 111  # 1、在全局定义x = 111
      def func():  # 2、定义func函数,此时已确定x取值先从func局部取值,局部未找到则向全局取值。
          print(x)  # 7、打印x,x此时从全局取值,结果为111
      ​
      def foo():  # 3、定义foo函数
          x = 222  # 5、在foo的局部名称空间内定义x = 222,并不会干扰到全局的x
          func()  # 6、调用func
      ​
      foo()  # 4、调用foo
      111
    • 示范四(难点):函数的嵌套定义

      input = 111
      def f1():
          def f2():
              print(input) # 222
          input = 222
          f2()
      f1()
    • 示范五(难点):逻辑错误

      x = 111
      def func():
          print(x)  # 报错(UnboundLocalError: local variable 'x' referenced before assignment)。
          x = 222
      func()

Python之禅的最后一句话。

import this
...
Namespaces are one honking great idea -- let's do more of those!

 

5、名称空间总结与注意事项

  • 三种名称空间必须要有的是 : 内置名称空间, 全局名称空间

  • 重要概念:名称空间的嵌套关系是在函数定义阶段(检测语法)时确定的,与函数调用的位置无关,与函数定义位置有关

    函数的取值位置是个很绕的知识点,最好一步一步分析函数执行步骤,与惯性思维作斗争。

    • 示例一

      代码一:
      x = 111  # 1、首先在全局定义x = 111
      def func():  # 2、定义func,此时已确定x取值先从func局部取值,局部未找到则向全局取值。
          print(x)  # 5、x向全局取值,此时x = 222,所以最终结果为222。
      ​
      x = 222  # 3、此时全局定义x = 222
      func()  # 4、调用func
      222
      ​
      代码二:
      x = 111  # 1、首先在全局定义x = 111
      def func():  # 2、定义func,此时已确定x取值先从func局部取值,局部未找到则向全局取值。
          print(x)  # 4、x向全局取值,此时x = 111,所以最终结果为111。
      ​
      func()  # 3、调用func。
      x = 222  # 5、此时在全局定义x = 222,但func函数已经调用,函数内的x已经取值为111。
      111
    • 示例二

      x = 1  # 1、在全局定义x = 1
      def func():  # 2、定义func函数,此时已确定x取值先从func局部取值,局部未找到则向全局取值。
          print(x)  # 7、打印x,x此时从全局取值,结果为1
      ​
      def foo():  # 3、定义foo函数
          x = 222  # 5、在foo的局部名称空间内定义x = 222,并不会干扰到全局的x
          func()  # 6、调用func
      ​
      foo()  # 4、调用foo
      1
    • 示例三:

      input = 111  # 1、在全局定义input = 111。
      def f1():  # 2、定义f1函数。
          def f2():  # 4、在f1局部名称空间内定义f2。
              input = 333  # 7、在f2局部名称空间内定义input = 333。
              print(input) # 8、input先在当前所在名称空间内取值,此时input值为333。
          input = 222  # 5、在f1局部名称空间内定义。
          f2()  # 6、调用f2,执行函数体代码。
      ​
      f1()  # 3、调用f1,执行f1函数体代码。
      333
    • 示例四

      count = 1
      def func():
          count = 100
          print(count)  # 直接在当前名称空间取值,count为100。
      func()
      100
    • 示例五:

      def f1():
          m=111
          return m
      
      def f2():
          print(res)  # 此时已确定res取值先从f2局部取值,局部未找到则向全局取值。
      
      def f3():
          print(res)  # 此时已确定res取值先从f3局部取值,局部未找到则向全局取值。
      
      res=f1()  # f1() = m = 111
      f2()
      f3()
      
      111
      111
    • 示例六:

      x=111
      
      def f1():
          print(x)  # 定义阶段已经确定先从局部取值,但是局部x是先取值再定义,所以保存
          x = 222
      
      f1()
      UnboundLocalError: local variable 'x' referenced before assignment
    • 示例七:

      m = [111,]
      def func(y):
          m.append(333)
          print(y)
      
      func(m)
      m.append(222)
      print(m)
      
      [111, 333]
      [111, 333, 222]

6、三种名称空间的取值顺序

按照LEGB原则就近取值,取值顺序单向不可逆。

LEGB原则:

  • LEGB代表名字查找顺序 :Local本地 --> Enclosed嵌套函数的外层函数内部 --> Global全局 -->Builtin内置

  • L ——Local(function); 函数内的名字空间

  • E —— Enclosing function locals;外部嵌套函数的名字空间(例如closure)

  • G—— Global(module); 函数定义所在模块(文件)的名字空间

  • B —— Builtin(Python)Python内置模块的名字空间

(从局部开始找时)局部名称空间 --> 全局名称空间 --> 内置名称空间

input = 333
print(input) # 此时是从全局开始取值,input()函数则不会被查找到
# 333

 

三、作用域

什么是作用域?

  • 作用域就是根据名称空间的范围和特点的不同进一步做了归类。

  • 查看作用域:: globals( ), locals( )

变量的生效范围,分为全局作用域和局部作用域。

全局作用域与局部作用域

全局作用域

  • 包含:全局名称空间、内置名称空间

  • 特点:全局存活,全局有效

     

    # x就属于全局名称空间,在全局名称空间和局部名称空间都能访问到。
    x = 10
    def f1():
        print(x)
    print(x)
    f1()
    10
    10

    globals()

    返回包含当前范围的全局变量的字典。

    x = 111
    print(globals())
    
    {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000002061940>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/desktop/code.py', '__cached__': None, 'x': 111}

     

局部作用域分为:

  • 包含:局部名称空间

  • 特点:临时存活,局部有效

  • 局部作用域可以引用全局作用域的变量,但不可以改变

    x = 10
    def f1():
        x = 20
    ​
    f1()
    print(x)

locals()

返回包含当前作用域的局部变量的字典。

x = 111
def func():
    a = 10
    b = 20
    print(locals()) #{'a': 10, 'b': 20}
func()

 

四、globalnonlocal关键字

1.global关键字

  • 使用方式:global x:声明这个x是全局的名字x(x也可以是其他变量)

  • 针对的是不可变类型的值

只能在局部名称空间使用。在局部声明一个全局作用域的变量。在 global 语句中列出的名称不得在同一代码块内该 global 语句之前的位置中使用。

global用处:如果想在局部修改全局的名字对于的值,且对应得一定要是不可变类的值,这个时候就使用global(注意:global作用就是争对局部中修改全局中不可变类型的值)

代码一:
在全局空间定义一个变量"x",函数"foo",在函数内部修改"x"的值
x = 111
def foo():
    x = 222
    
foo()
print(x)   #111
#发现并无软用
​
代码二:
使用"global"关键字之后
x = 111
def foo():
    global x  # 声明下面的"x"属于全局
    x = 222
    
foo()
print(x)  #222
#发现成功了

 

global 语句中列出的名称不得被定义为正式形参,不也得出现于 for 循环的控制目标、class 定义、函数定义、import 语句或变量标注之中。

局部名称空间不能修改全局名称空间的不可变数据类型的值,只能引用。

c = 1
def func():
    c += 1      # 不可更改,在更改时会从先局部名称空间取c的值,然后再赋值给c,造成先引用、后定义的保错
    print(c)
func()
#  local variable 'count' referenced before assignment

 

对于可变类型,也不能使用先取值后修改再赋值给原变量名这种方式,而是使用内置方法来修改。

代码一:
c = [1,2,3]
def func():
    c += [3,4,5]  # 相当于 c = c + [3,4,5]
    print(c)
func()
UnboundLocalError: local variable 'c' referenced before assignment
​
代码二:
c = [1,2,3]
def func():
    c.extend([3,4,5])
    print(c)
func()
[1, 2, 3, 3, 4, 5]

使用global关键字,可以在局部修改一个全局变量。

c = 1
def func():
    global c #声明下面的c属于全局
    c += 1
    print(c)
func()
2

 

2.nonlocal关键字

  • 使用方法 : nonlocal x

  • 作用:修改该函数外层函数包含的变量名对应的值, 也是针对不可变类型nonlocal起码在第二次嵌套函数内使用才有意义,能改变一个外层函数的非全局变量)

  • 注意 : 只能在局部名称空间中查找, 先从外部嵌套的函数找, 没找到再往上找, 如果都没找到则报错,不会去全局修改变量值

代码一:
不做任何处理,此函数应该是打印"f2"函数下的"x = 333"
x = 111
def f1():
    x = 222
    def f2():
        x = 333
        def f3():
            x = 444
        f3()
        print(x)  #333
    f2()
f1()  # 333
​
代码二:
使用"nonlocal"关键字,nonlocal起码在第二次嵌套函数内使用才有意义,能改变一个外层函数的非全局变量
x = 111
def f1():
    x = 222
    def f2():
        x = 333
        def f3():
            nonlocal x  #将下面的"x = 444"变成了上一层的"x"的值(ps:逻辑上是x=333 被修改为x=444,)
            x = 444
        f3()
        print(x) 
    f2()
f1()  # 444
​
代码三:
如果外层函数都没有"x"这个值,报错
x = 111  #并不会去全局修改
def f1():
    def f2():
        def f3():
            nonlocal x
            x = 444
        f3()
        print(x)
    f2()
f1()  #报错,SyntaxError: no binding for nonlocal 'x' found

 

只能在函数嵌套定义的内层函数中使用,在第一层局部名称空间内使用会报错。用作函数嵌套定义中,在内层函数中声明一个外层局部名称空间的变量。

nonlocal 语句中列出的名称不得与之前存在于局部作用域中的绑定相冲突。

x = 111
​
def f1():
    x = 222
​
    def f2():
        nonlocal x
        x = 333 #
​
    f2()
    print(x)
​
f1()
333

相关文章:

  • python之面向过程编程思想与匿名函数及其应用
  • docker 之Dockerfile
  • python之logging 模块(简洁版)
  • Docker 搭建 Redis Cluster 集群环境
  • Docker 容器编排利器 Docker Compose
  • Docker Swarm 集群环境搭建及弹性服务部署
  • Docker Compose 搭建 Redis Cluster 集群环境
  • 计算机初识
  • 计算机硬件五大单元
  • 计算机硬件组成详解
  • 计算机硬盘接口及操作系统
  • 计算机核心概念之进程、线程、进程池、进程三态、同步、异步、并发、并行、串行...
  • DHCP工作过程的六个主要步骤
  • TCP如何保证传输可靠性
  • TCP协议-如何保证传输可靠性
  • Android 架构优化~MVP 架构改造
  • download使用浅析
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • Javascript弹出层-初探
  • Java比较器对数组,集合排序
  • Java-详解HashMap
  • js算法-归并排序(merge_sort)
  • MySQL几个简单SQL的优化
  • Redis学习笔记 - pipline(流水线、管道)
  • Redux 中间件分析
  • 从setTimeout-setInterval看JS线程
  • 搭建gitbook 和 访问权限认证
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 目录与文件属性:编写ls
  • 前端设计模式
  • 前端自动化解决方案
  • 使用权重正则化较少模型过拟合
  • 物联网链路协议
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 组复制官方翻译九、Group Replication Technical Details
  • ​linux启动进程的方式
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​一、什么是射频识别?二、射频识别系统组成及工作原理三、射频识别系统分类四、RFID与物联网​
  • (003)SlickEdit Unity的补全
  • (16)Reactor的测试——响应式Spring的道法术器
  • (3)选择元素——(17)练习(Exercises)
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (超详细)语音信号处理之特征提取
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • .net core 控制台应用程序读取配置文件app.config
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .Net 高效开发之不可错过的实用工具