名称空间与其查找顺序、作用域、global与nonlocal关键字的使用、函数名的多种用法以及函数的嵌套

今日内容总结

      在昨日的学习中,我们初步了解了函数,以及函数的使用,参数的介绍。而昨日的重点正是参数。今日的学习,依旧是函数,学习函数的用法。

名称空间

      名称空间,其实就是存放变量名与变量值绑定关系的地方。在程序执行期间最多会存在三种名称空间。分别是内置名称空间,全局名称空间,局部名称空间。接下来,就是对这三种名称空间的介绍。

      内置名称空间。其实就是python解释器提前给我们定义好的,内置函数名与函数体代码绑定的空间。因而是第一个被加载的名称空间,用来存放一些内置的名字,比如内建函数名len(),print()等。

      全局名称空间。我们在python文件中编写的代码运行产生的名字都会存到全局名称空间。是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中。例如:

  # 全局名称空间
  name = 'jason'
  def output():
      pass
  
  if i < 3:
      i = 1

  for i in range(5):
      pass

  while i < 3:
      i = 1

  # 以上类型中,无论是函数名与函数体的关系,还是变量名与值的关系。都是在全局空间的

      局部名称空间。就是函数体代码运行产生的空间。函数的形参、函数内定义的名字都会被存放于该名称空间中,只要在函数内的名称空间就是局部的。例如:

  # 局部名称空间
  def output():
      name = 'jason'
  # 如以上这种,在函数体代码中产生的变量名与变量值之间对应的关系,都是存放在局部名称空间的

      生命周期。

      1.内置名称空间:在python解释器启动时创建,在解释器关闭时销毁。

      2.全局名称空间:在执行文件时,创建全局名称空间,所有文件中的代码全部执行完毕后,销毁名称空间(即解释器关闭时)

      3.局部名称空间:调用函数时创建,函数执行完毕时就销毁

名字的查找顺序

      名称空间是用来存放名字与值绑定关系的地方,而我们的名称空间分了三类,在查找的时候,就有查找顺序的出现。查找顺序如下:

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

      在全局名称空间时的查找顺序:全局名称空间-->内置名称空间

      当我们在查找名字时,一定要先搞清楚自己在哪个空间,因为不同的名称空间时可以有相同的名字的。如:

  # 当三个名称空间出现相同的变量名时,判断我们打印的到底是哪个空间的变量名
  len = '全局空间'
  def output():
      len = '局部空间'
      print(len)

  output()  # 局部空间
  print(len)  # 全局空间

  # 因为我们说过,调用函数,函数体才会执行,而函数体执行,就会产生局部名称空间。为了更方便理解,我们用了这样一个嵌套函数来举例

  x = 1  # 全局名称空间

  def a1():  # 定义一个函数a1
      x = 2  # 局部名称空间1
      def a2():  # 定义一个函数a2
          x = 3  # 局部名称空间2
          def a3(): # 定义一个函数a3
              x = 4  # 局部名称空间3
              print(x)  # 打印x
          a3()  # 调用a3
      a2()  # 调用a2


  a1()  # 调用a1

  # 以上代码,看似很复杂,但是我们之前说过,在查找名字时,一定要搞清楚自己所在的空间。以上代码相当于,在全局空间内有一个空间为局部名称空间1,而局部名称空间1内有一个局部名称空间2.局部名称空间2内有一个局部名称空间3。当我们不调用函数a1时。x = 1,而调用函数a1后,a1中的函数体代码开始执行。执行顺序为:首先,进入局部名称空间1,x = 2。随后调用函数a2,随后进入局部名称空间2,x = 3。随后调用函数a3,随后进入局部名称空间3,这时候,x = 4了,最后打印4。这样的顺序一定要知道。

  # 但是,如果我们a3中的函数体代码如下
  def a3():
      print(x)
      x = 4

  # 此时调用函数a1会报错提示local variable 'x' referenced before assignment。因为我们在检测语法时,发现f3的局部名称空间将来会有x,所以查找的时候就跟a3的局部名称空间要。这种特例只是为了理解名字查找顺序的。一般不会出现

作用域

      作用域就是名称空间能够作用的范围。


  # 内置名称空间:程序任意阶段任意位置均可使用(全局有效)

  # 全局名称空间:程序任意阶段任意位置均可使用(全局有效)

  # 局部名称空间:一般情况下只在各自局部名称空间中有效(局部有效)

global与nonlocal关键字

      gloabl关键字:局部修改全局不可变类型。global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用。如:

  # global关键字的使用
  age = 18

  def func2():
      # 明确声明使用全局的变量age(修改a的值)
      global age
      age = 19

  func2()
  print(age)  # 19
  
  # 而如果我们将global注释掉会发生什么呢
  age = 18


  def func2():
      # 明确声明使用全局的变量age(修改a的值)
      # global age
      age = 19


  func2()
  print(age)  # 18

  # 这里依旧是我们查找名字顺序,如果还不明白的话,再看看上面的解释

      nonlocal关键字:nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误。

  # nonlocal关键字的使用
  a = 1

  def func3():
      a = 10

      def inner():
          # 明确声明 要使用上一层的 a 如果上一层没有,则使用上上一层,但是不能使用全局中的a
          nonlocal a
          a = 100
          print(a)

      inner()
      print('这是func3中的a', a)

  func3()  # 100   这是func3中的a 100

  # 局部名称空间嵌套的情况下,内层如果想要修改外层。情况1数据是不可变类型:需要使用关键字nonlocal。情况2数据是可变类型:不需要使用关键字nonlocal。

函数名的多种用法

      函数名,类似于变量名,同时,函数名也有多种用法:

      1.函数名可以当做变量名赋值

  def output():
      print('jason nb')

  print(output)  # <function output at 0x0000018FB5941EA0>

  res = output

  print(res)  # <function output at 0x00000203ED5E1EA0>

  res()  # jason nb

  # 当函数名做变量名赋值时,在上述示例中,是让res也指向函数体代码。这时候res() = output()

      2.函数名可以当成函数的实参

  def output():
      print('jason nb')


  def func(a):
      print('jason handsome')
      print(a)
      a()


  func(output)  # jason handsome   <function output at 0x0000021FEF581EA0>   jason nb

  # 当我们的函数名当做别的函数的实参后,如上述代码中,可以在func调用a()。指向的就是实参的函数

      用法3:函数名可以当做函数的返回值

  def output():
      print('jason nb')
      return func


  def func():
      print('jason handsome')


  res = output()  # jason nb

  print(res)  # <function func at 0x000001D0D910BC80>

  res()  # jason handsome

  # 将函数名func当做返回值后,res接收函数名。res() = func()

      用法4:函数名可以作为容器类型的元素

  def output():
      print('jason nb')


  a = ['jason', 'nb', 666, output]

  print(a)  # 'jason', 'nb', 666, <function output at 0x0000017090501EA0>]

  a[-1]()  # jason nb

  # 容器类型:内部可以存档多个元素的数据类型>>>:列表、元组、字典

函数的嵌套

      函数的嵌套,就是说在函数中再定义函数,或者说,把一个函数定义在另一个函数的内部。嵌套函数是为函数内部服务的,比如减少代码的重复,想要调用函数,要使用函数名,内函数也一样。如果不用函数名调用内函数,内函数就永远不会执行。在上述的代码示例中我们提到过。对于函数的嵌套使用,只要搞清楚一个道理。那就是,函数只有在被调用的时候,才会执行函数体代码。也就是说,就算你嵌套了函数,如果不调用嵌套函数的话,嵌套了跟没嵌套一样。因为它不会执行。

posted @ 2022-03-17 17:56  くうはくの白  阅读(57)  评论(0)    收藏  举报