Python 闭包的相关知识

Python 闭包相关知识

闭包字面意思,是封闭的东西,为了保证数据的安全。

用案例解释闭包:整个历史中的某个商品的平均收盘价。什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格。

​ 比如大众推出了一款新车:小白轿车。

​ 第一天价格为:100000元,平均收盘价:100000元

​ 第二天价格为:110000元,平均收盘价:(100000 + 110000)/2 元

​ 第三天价格为:120000元,平均收盘价:(100000 + 110000 + 120000)/3 元

​ …………

实现以上需求,方案一:不能保证数据安全
l1 = []		# 全局变量,数据不安全,容易被误操作改变。
li = []		# 相似的变量
def make_averager(new_value):
    l1.append(new_value)
    total = sum(l1)
    return total / len(l1)    
print(make_averager(100000))	# 100000.0
print(make_averager(110000))	# 105000.0
l1.append(34556)	# 本来要对li进行操作,但是不小心追加错了,造成最终结果出错。
print(make_averager(120000))	# 83364.0
方案二:使用闭包,外部无法对数据修改,只能以特定的渠道进行调用。
def make_averager():
    l1 = []                         # 将l1放置在函数内,变成了局部变量,
    def averager(new_value):        # l1在这里被嵌套函数averager调用了,
        l1.append(new_value)        # 列表l1和函数averager之间产生了一个绑定关系,
        return sum(l1) / len(l1)
    print(locals())
    return averager
avg = make_averager()               # 接下来嵌套函数averager被赋值给了avg变量,
print(avg(100000))                  # 所以l1虽然在全局变量列表中已经消失,不可调用,但却真实存在,
# l1.append(54464)                  # 这行代码调用将出错
print(avg(110000))                  # 只有通过avg变量调用才能对l1进行改变,
print(avg(90000))                   # 这个现象就是闭包。
print(globals())
  1. 闭包的定义:

    • 闭包是嵌套在函数中的函数。
    • 内层函数对外层函数的变量(非全局变量)的引用(使用),就会形成闭包。
  2. 闭包的现象:

    • 被引用的非全局变量也称作*变量,这个*变量会与内层函数产生一个绑定关系,*变量不会在内存中消失
  3. 闭包的作用:保存局部信息不被销毁,保证数据的安全性。

  4. 闭包的应用:

    • 可以保存一些非全局变量但是不易被销毁、改变的数据。
    • 装饰器。
  5. 判断是否有闭包:

    • 使用代码判断是否存在闭包现象,只需要判断是否存在有“*变量”即可,判断代码为:

      def func(a, b):		# 这段代码虽然看上去变量a,b为全局变量,
          def inner():	# 但是传入函数内将生成一个局部变量a,b
              print(a)	# 函数内部的a,b变量和外面的并不是同一个
              print(b)	
          return inner
      a = 2
      b = 3
      ret = func(a, b)	# 所以这条命令之后将形成一个闭包现象 
      print(ret.__code__.co_freevars)	# 以这条指令判断是否存在“*变量”
      >>>('a', 'b')		# 运行结果有a,b两个变量属于*变量,可以断定上面代码存在闭包现象。
      
  6. 查看函数相关的其他参数:

    • 有一些函数的属性是可以获取到此函数是否拥有*变量的,如果此函数拥有*变量,那么就可以侧面证明其是否是闭包函数了,以下还有一些参数,仅供了解:

      def make_averager():
          series = []
          def averager(new_value):
              series.append(new_value)
              total = sum(series)
              return total/len(series)
          return averager
      avg = make_averager()
      
      # 函数名.__code__.co_freevars 查看函数的*变量
      print(avg.__code__.co_freevars)  # ('series',)
      # 函数名.__code__.co_varnames 查看函数的局部变量
      print(avg.__code__.co_varnames)  # ('new_value', 'total')
      # 函数名.__closure__ 获取具体的*变量对象,也就是cell对象。
      # (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,)
      # cell_contents *变量具体的值
      print(avg.__closure__[0].cell_contents)  # []
      
上一篇:关于Python闭包的一切


下一篇:Day 13 匿名函数 :内置函数: 闭包