python 学习汇总53:闭包(中级学习- tcy)

闭包的定义,及基本概念;实例;闭包定义中的典型错误分析及解决办法;闭包在实践的应用。
 

闭包                 创建时间:2018/8/11  修改时间:2018/11/18


 1.定义: 

闭包:是由函数及其相关的引用环境组合而成实体(即:闭包=函数+引用环境)
内部函数引用外部作用域(非全局)变量,会把引用环境和函数体打包成一个整体(闭包)返回,内部函数被认为是闭包(closure).
函数:
可作另一个函数参数或返回值,赋给变量。函数可嵌套定义;
每次调用ExFunc函数返回新闭包实例,实例之间是隔离;

一个函数可返回一个计算结果或函数。返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量
引用环境
指在程序执行中的某个点所有处于活跃状态的约束(变量名字和其所代表的对象之间的联系)所组成的集合。  

2.实例:

def ExFunc(n): # 函数返回的就是闭包
   sum = n

   def InsFunc(): # 内嵌函数是闭包
     return sum + 1 # 引用到外层函数中的局部变量sum

   return InsFunc # 返回值是函数

myFunc = ExFunc(10) # 返回新闭包实例
a1 = myFunc()    # 11
myAnotherFunc = ExFunc(20) # 返回新闭包实例
a2 = myAnotherFunc()    # 21
print(a1, a2)

实例2
def add(x):
  def adder(y):
    return x + y
  return adder

a = add(8)
b=add
type(a)    #<type 'function'>
a.__name__ #'adder'
a(10)    #18
b(8)(10) #18
2.使用闭包注意事项: 
2.1.闭包中不能修改外部作用域局部变量

实例1:-每次调用闭包函数时都对变量a进行递增的操作
#使用闭包时经典错误代码
def foo():
  a =    1 
  def bar():
    # nonlocal a
    a = a + 1 #修改外部作用域局部变量
    #python规则指定所有在赋值语句左面的变量都是局部变量
    return a
   return bar

c = foo()
print(c()) #错误

#解决方法
def foo():
  a = [1]
  def bar():
    a[0] = a[0] +    1
    return a[0]
  return bar
 2.2. 循环结束循环体中临时变量i不会销毁存在于执行环境中:
注意:1)返回闭包时:返回函数不要引用任何循环变量,或后续会发生变化变量。
2)如要引用循环变量
方法1:该函数创建一个参数,用这个参数绑定到循环变量 ;如实例  1
方法2:迭代 方法;参见实例   2
再创建一个函数,用该函数的参数绑定循环变量当前的值,
无论该循环变量后续如何更改,已绑定到函数参数的值不变   

 实例2: 

#python的函数只有在执行时,才会去找函数体里的变量的值。
lst = []
for i in range(3):
   def foo(x):
     print (x + i)
   lst.append(foo)

for f in lst:
   f(2) #4,4,4

#解决方法
for i in range(3):
   def foo(x,y=i):
     print( x + y)
   lst.append(foo)  

 实例3 : 

def  count():
   lst = []
   for i in range(1, 4):
     def f():
       return i*i
     lst.append(f)
       return lst

f1, f2, f3 = count()
f1(),f2(),f3() #9,9,9

#解决办法
def count():
   def f(j):
     def g():
       return j*j
     return g

   lst = []
   for i in range(1, 4):
      lst.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
      return lst

#  结果:
f1, f2, f3 = count()
f1(),f2(),f3() #1,4,9

 3.作用
闭包主要是在函数式开发过程中使用。
用途1 

当闭包执行完后,仍然能够保持住当前的运行环境。
希望函数每次执行结果基于这个函数上次的运行结果。

例棋盘游戏:
棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向(direction),
步长(step),该函数控制棋子的运动。棋子运动的新的坐标除了依赖于方向和步长以外,当然还要根据原
来所处的坐标点,用闭包就可以保持住这个棋子原来所处的坐标。

origin = [0, 0] # 坐标系统原点
legal_x = [0, 50] # x轴方向的合法坐标
legal_y = [0, 50] # y轴方向的合法坐标

def create(pos=origin):
   def player(direction,step):
      # 先判断参数direction,step合法性,direction不能斜走step不能为负
      # 对新生成的x,y坐标合法性判断处理,
      new_x = pos[0] + direction[0]*step
      new_y = pos[1] + direction[1]*step
      pos[0] = new_x
      pos[1] = new_y #注意!此处不能写成 pos = [new_x, new_y]
      return pos
   return player

player = create() # 创建棋子player,起点为原点
print player([1,0],10) # 向x轴正方向移动10步
print player([0,1],20) # 向y轴正方向移动20步
print player([-1,0],10) # 向x轴负方向移动10步

输出:
[10, 0]
[10, 20]
[0, 20]

 用途2: 

闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,
我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。
比如有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行。
def make_filter(keep):
    def the_filter(file_name):
        file = open(file_name)
        lines = file.readlines()
        file.close()
        filter_doc = [i for i in lines if keep in i]
        return filter_doc
    
    return the_filter
    

# 取得文件"result.txt"中含有"pass"关键字的行
filter = make_filter("pass")
filter_result = filter("result.txt")

 

猜你喜欢

转载自blog.csdn.net/tcy23456/article/details/84204184