名称空间与作用域,函数对象,函数嵌套,闭包函数 - 图1

名称空间

什么是名称空间
namespaces名称空间:用于存放变量名与变量值绑定关系的地方(类似于民政局)

名称空间的分类

  • 内置名称空间:存放内置的名字(python解释器提前定义好的 )

    • 生命周期:python解释器启动则产生,关闭则销毁
  • 全局名称空间:在py文件中顶格编写的代码运行之后都会存入全局名称空间

    • 生命周期:运行python文件时则产生,python文件运行完毕则销毁
  • 局部名称空间:函数体代码运行之后产生的都是局部名称空间

    • 生命周期:调用函数则产生,函数调用完毕则销毁
  1. # 内置名称空间:
  2. input
  3. <built-in function input> # built-in:内置
  4. # 全局名称空间:
  5. x = 10 # 变量名X
  6. y = 20 # 变量名y
  7. f 1 > 0:
  8. z = 30
  9. with open('a.txt', mode='wt') as f:
  10. a = 333
  11. while True:
  12. c = 444
  13. # 以上都是属于顶格的
  14. # 局部名称空间:
  15. x = 10 # 全局名称空间
  16. def foo(m): # 全局名称空间
  17. m = 111 # 调用函数时,才会执行函数代码,名字m和n都存放于该函数的局部名称空间中
  18. n = 222 # 函数内
  19. foo(111) # 调用产生函数名称空间

名称空间的加载顺序:
内置名称空间—->全局名称空间—->局部名称空间

名称空间的查找顺序:
查找一个名字,必须从三个名称空间之一找到,要先确定自己当前在哪,基于当前所在的位置向外查找

如果当前在局部查找,查找顺序为:
局部名称空间>>>>全局名称空间>>>>内置名称空间

如果当前在全局查找,查找顺序为:
全局名称空间>>>>内置名称空间

  1. len = 10 # 全局名称空间
  2. def func(): # 函数
  3. len = 20
  4. # print(len) # 站在局部查找,找到的是20
  5. func() # 调用产生局部名称空间
  6. print(len) # 站在全局找,全局有找全局,全局没有找内置
  7. # 局部名称空间的嵌套
  8. '''函数在定义阶段名字的查找顺序就已经固定死了(特例)'''
  9. x = 111
  10. def f1():
  11. x = 222
  12. def f2():
  13. x = 333
  14. def f3():
  15. x = 444
  16. def f4():
  17. x = 555
  18. print(x)
  19. # x = 555 特例
  20. f4()
  21. f3()
  22. f2()
  23. # 通过调用f1 执行所有的函数
  24. f1()

名称空间与作用域,函数对象,函数嵌套,闭包函数 - 图2

作用域

作用域:名称空间所能够作用的范围

  • 全局作用域:内置名称空间 or 全局名称空间
    • 特点:全局存活(除非被删除,否则在整个文件执行过程中存活)
      • 程序任何阶段任何位置均可使用(全局有效


  • 局部作用域:局部名称空间
    • 特点:临时存活(即在函数调用时临时生成,函数调用结束后就释放)
      • 一般情况下只在各自的局部名称空间中有效(局部有效)

名称空间与作用域的关系是在函数定义阶段就确立的,与什么时候调用以及调用位置无关

  1. # 例1
  2. x = 111
  3. def f1():
  4. print(x) # 在定义阶段就已经确定了要去全局找
  5. def f2():
  6. x = 222
  7. f1()
  8. f2() # 111
  9. # 例2
  10. x = 111
  11. def f1():
  12. print(x)
  13. x = 222
  14. f1() # 报错
  15. """
  16. 名称空间与作用域的关系是在函数定义阶段扫描语法的时候就确立的
  17. """

global关键字

在函数内,无论嵌套多少层,都可以查看到全局作用域的名字,若要在函数内修改全局名称空间中名字的值,当值为不可变类型时,则需要用到global关键字

  1. # 例1:
  2. l = [] # 全局变量可变类型
  3. def func():
  4. l.append(1111) # 局部改全局
  5. func() # 调func执行append
  6. print(l) # [1111]
  7. """
  8. 当全局变量是可变类型的时候,局部是可以直接改的
  9. 要想在局部修改一个全局的不可变类型,可以用global修改
  10. """
  11. # 例2:
  12. x = 111 # 全局变量不可变类型
  13. def func():
  14. global x # 声明x是属于全局的,是可以改变的
  15. x = 222
  16. func()
  17. print(x) # 222

nonlocal关键字

对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外层嵌套函数定义的作用域(非全局)

  1. x = 111
  2. def f1():
  3. # x = 222
  4. def f2():
  5. nonlocal x # 声明变量名是来自于外层函数的,不是全局
  6. x = 333 # 想在这个位置把x=222改为x=333,就要用nonlocal
  7. f2()
  8. print(x)
  9. f1()
  10. """
  11. 如果想要在内部的局部修改外部局部的不可变类型数据
  12. 需要关键字nonlocal声明
  13. """

名称空间与作用域,函数对象,函数嵌套,闭包函数 - 图3

函数对象

在python中,函数是第一类对象,即函数可以当做数据传递,函数又称第一等公民
本质:函数可以当变量用

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

  1. def func():
  2. print('from func')
  3. a = func # 可以赋值给其它变量
  4. print(a)
  5. a() # 本质就是在调用func函数
  6. '''
  7. <function func at 0x7ff6b61b91f0>
  8. from func
  9. '''

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

  1. def func():
  2. print('from func')
  3. def foo(x):
  4. print(x)
  5. x() # 可以加()触发内部代码的运行
  6. foo(func)
  7. '''
  8. <function func at 0x7fbe691b91f0>
  9. from func
  10. '''

3.函数名可以当做函数的返回值

  1. def func():
  2. print('from func')
  3. def foo(x): # 把func丢进去
  4. return x # 紧接着又扔出来func
  5. res = foo(func)
  6. print(res) # <function func at 0x7fc9290b91f0>

4.函数名可以当做容器类型(内部可以存放多个数据)的元素

  1. def func():
  2. print('from func')
  3. l = [func,] # 列表调用
  4. print(l)
  5. l[0]()
  6. '''
  7. [<function func at 0x7f7ae29b91f0>]
  8. from func
  9. '''
  10. # 容器类型的元素的应用:购物车程序
  11. def register():
  12. print('注册功能')
  13. def login():
  14. print('登录功能')
  15. def shopping():
  16. print('购物功能')
  17. def transfer():
  18. print('转账功能')
  19. def withdraw():
  20. print('提现功能')
  21. def check_order():
  22. print('查看订单')
  23. func_dic = {'1':register,
  24. '2':login,
  25. '3':shopping,
  26. '4':transfer,
  27. '5':withdraw,
  28. '6':check_order
  29. }
  30. while True:
  31. print("""
  32. 1.注册功能
  33. 2.登录功能
  34. 3.购物功能
  35. 4.转账功能
  36. 5.提现功能
  37. 6.查看订单
  38. """)
  39. choice = input('请输入功能编号>>>:').strip()
  40. # 判断用户输入的编号在不在字典的k中
  41. if choice in func_dic:
  42. # 根据键获取值(函数名)
  43. func_name = func_dic.get(choice)
  44. # 函数名加括号调用
  45. func_name()
  46. else:
  47. print('功能编号不存在')

名称空间与作用域,函数对象,函数嵌套,闭包函数 - 图4

函数嵌套

函数的嵌套调用

  1. # 嵌套调用:函数内部调用其他函数
  2. def max2(x, y): # 比较两个值的大小
  3. if x > y:
  4. return x
  5. else:
  6. return y
  7. def max4(a, b, c, d): # 求四个值的最大值
  8. res1 = max2(a, b)
  9. res2 = max2(res1, c)
  10. res3 = max2(res2, d)
  11. return res3
  12. print(max4(11, 99, 33, 12)) # 99
  13. '''
  14. 把一个大功能拆解成几个小功能,然后再把每个小功能分别去实现,最后在拼接回来
  15. '''

函数的嵌套定义

  1. # 函数体内部定义其他函数
  2. # 将复杂的功能全部隐藏起来,暴露一个简单的接口
  3. def f1():
  4. x = 10 # 变量丢到函数里面是一个封闭的效果
  5. def f2():
  6. print('from f2')
  7. print(x) # 这行代码是站在内部去看的,可以看见
  8. print(f2) # 这行代码也是站在内部去看的,可以看见
  9. f1()
  10. # print(x) # 报错,这行代码是站在外面去看的,是看不见的
  11. # print(f2) # 报错,这行代码是站在外面去看的,是看不见的
  12. '''
  13. 10
  14. <function f1.<locals>.f2 at 0x7f8d84908550>
  15. '''
  16. # 函数层级带来的变量访问的限制

名称空间与作用域,函数对象,函数嵌套,闭包函数 - 图5

闭包函数

闭:定义在函数内部的函数
包:内部函数使用了外部函数名称空间中的名字
只有符合上述两个特征的函数才可以称之为”闭包函数”

  1. def outer():
  2. x = 111 # 为index函数的函数体代码传参
  3. def index():
  4. print(x)
  5. # 以后无论index在哪调,它访问的x以定义阶段为准。
  6. # 示例
  7. def outer(): # outer最外层函数
  8. x = 111
  9. def wrapper(): # 把wrapper闭到了outter里面了,wrapper就只能在里面用
  10. print(x)
  11. return wrapper # 千万别加括号,要想把wrapper从里面扔出来用就需要用return
  12. f = outer() # 把一个内部函数扔到全局,这个内部函数就打破了层级限制,可以在任意位置调用
  13. print(f) # <function outer.<locals>.wrapper at 0x7fbedc908550>
  14. def foo():
  15. x = 222
  16. f() # 此时调f就相当于调wrapper
  17. foo() # 111

为函数代码体传参的方案

方案一:函数体代码需要用到数据,直接在括号内定义形参即可

  1. def wrapper(x):
  2. print(x)
  3. wrapper(111)
  4. wrapper(222)
  5. wrapper(333)

方案二:利用闭包函数

  1. def outter(x): # 为了给wrapper函数传参只能把它扔进去,wrapper本来是顶级的
  2. def wrapper():
  3. print(x)
  4. return wrapper # 参数传完就把wrapper用return扔出去
  5. f1 = outter(111) # 再想拿到wrapper,调outter拿到返回值,f1相当于当初的wrapper
  6. f1() # 111
  7. f2 = outter(222)
  8. f2() # 222