Python 名称空间与作用域、闭包与装饰器

时间:2023-03-09 15:38:48
Python 名称空间与作用域、闭包与装饰器

Python 的名称

Python 的名称(Name)是对象的一个标识(Identifier)。我们知道,在 Python 里面一切皆对象,名称就是用来引用对象的。说得有点玄乎,我们以例子说明。

例如,在a = 2这个语句中,2是个存储在内存中的一个对象,名称a则会引用2这个对象,“引用”的含义是指可以通过名称a来使用2这个对象。我们可以使用id()函数来获取对象的地址。

Python 名称空间与作用域、闭包与装饰器

可以看到,两都均指向同一个对象

Python 的名称与对象

起初,名称a引用对象2; 
然后,执行操作a = 1,这时对象1被创建,名称a引用对象1,所以id(a)id(1)输出相同的地址; 但是对象2没有名称引用,继而丢弃在内存空间中成为垃圾。

这个例子也展示了 Python 在执行变量的赋值时,并不会重复创建一个对象的事实。名称采用动态绑定的机制使得 Python 更加高效,同一个名称可以引用不同类型的对象。

Python 名称空间与作用域、闭包与装饰器

Python 的名称空间

名称空间是名称到对象的映射。在 Python 中,名称空间采用字典来实现。Python 的名称空间包括:

  • 内置名称空间,例如,内置名称空间包含 Python 的内置函数,如,abs()
  • 模块名称空间,全局名称空间,在模块内定义的名称
  • 局部名称空间,例如,在函数(function)或者类(class)被调用时,其内部包含的名称

不同的名称空间内的名称不会相互冲突,即是它们采用相同的名称。这也正是名称空间的作用。

内置名称空间在 Python 解释器启动时就创建了,直到 Python 解释器退出时内置名称空间才失效。这使得我们可以在程序的任何位置使用内置名称空间内的名称,例如,id()print()等函数。 
模块名称空间当模块被引用时创建,直到 Python 解释器退出时模块名称空间才失效。 
函数名称空间在函数被调用时创建,函数返回后失效。

Python 变量的作用域

例子

Python 名称空间与作用域、闭包与装饰器

在这个例子中,名称a在全局名称空间中,名称b在函数outer_function的局部名称空间,名称c则在函数inner_func的局部名称空间。 
当我们在函数inner_func时,c是个局部的名称,b是个非局部的名称,而a则是个全局的名称。在函数inner_func中,我们可以对c进行读取操作和赋值操作,而只能对ba进行读取操作。当对b进行赋值时,一个新的名称将会被创建,这个新的名称处于inner_func函数局部名称空间中。对a进行赋值时也会在局部名称空间中创建一个新的名称。

如果想要对全局变量进行修改以便下次调用,可以用global xxx(变量名)即可。

Python 闭包

如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数。

在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

Python 名称空间与作用域、闭包与装饰器

在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。

Python 名称空间与作用域、闭包与装饰器

闭包有啥用??!!

  装饰器!!!装饰器是做什么的??其中一个应用就是,我们工作中写了一个基础平台功能,我们想让别的部门可以调用,但是原先基础平台又不允许改代码,这就是装饰器的作用

   面向对象!!!经历了上面的分析,我们发现外函数的临时变量送给了内函数。大家回想一下类对象的情况,对象有好多类似的属性和方法,所以我们创建类,用类创建出来的对象都具有相同的属性方法。闭包也是实现面向对象的方法之一。在python当中虽然我们不这样用,在其他编程语言入比如avaScript中,经常用闭包来实现面向对象编程

   实现单利模式!! 其实这也是装饰器的应用。单利模式毕竟比较高大,,需要有一定项目经验才能理解单利模式到底是干啥用的,我们就不探讨了。

Python 装饰器

Python 名称空间与作用域、闭包与装饰器

当写完这段代码后(函数未被执行、未被执行、未被执行),python解释器就会从上到下解释代码,步骤如下:

  1. def w1(func):  ==>将w1函数加载到内存
  2. @w1
  3. @w1就相当于    #下面定义的函数名 = w1(定义的函数名作为实参),当最下面一行 调用 f1()的函数的时候才是会去闭包里的inner函数执行相应的操作

没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。

从表面上看解释器着实会执行这两句,但是 @w1 这一句代码里却有大文章,@函数名 是python的一种语法糖。

如上例@w1内部会执行一下操作:

    • 执行w1函数,并将 @w1 下面的 函数 作为w1函数的参数,即:@w1 等价于 w1(f1)
      所以,内部就会去执行:
      def inner:
      return f1()   # func是参数,此时 func 等于 f1
      return inner     # 返回的 inner,inner代表的是函数,非执行函数
      其实就是将原来的 f1 函数塞进另外一个函数中
    • 将执行完的 w1 函数返回值赋值给@w1下面的函数的函数名
      w1函数的返回值是:
      def inner:
      return 原来f1()  # 此处的 f1 表示原来的f1函数
      然后,将此返回值再重新赋值给 f1,即:
      新f1 = def inner:
      return 原来f1()
      所以,以后业务部门想要执行 f1 函数时,就会执行 新f1 函数,在 新f1 函数内部先执行验证,再执行原来的f1函数,然后将 原来f1 函数的返回值 返回给了业务调用者。
      如此一来, 即执行了验证的功能,又执行了原来f1函数的内容,并将原f1函数返回值 返回给业务调用着