Python学习(十五)--闭包的用法

1.一切皆对象

python支持函数式编程,但不一定非要用函数式编程
其他语言里,函数只是一段可执行的代码,并不是对象
python里,一切皆对象,简化了学习难度
例如:

def f1():
  pass
a = f1()
print(type(f1))
==>

在其他语言中把一个函数赋值给一个变量是做不到的;
python可以将一个函数作为另一个函数的参数,传递到另外的函数里,也可以把一个函数当做另外一个函数的返回结果

2.闭包是什么

(1)

def curve_pre():
    def curve():
        pass

curve()
==>
Traceback (most recent call last):
  File ".\c14.py", line 5, in 
    curve()
NameError: name 'curve' is not defined
#直接调用会报错函数未定义

(2)

def curve_pre():
    def curve():
        print('This is a funciton')
    return curve

curve_pre()
print(type(curve_pre()))
==>

#直接调用curve_pre,看不到返回值,其实curve_pre返回了一个函数即curve,
#因此想要看到This is a funciton,需要先把curve赋值给一个变量,再通过这个变量,对curve进行调用,如下:

def curve_pre():
    def curve():
        print('This is a funciton')
    return curve
f = curve_pre()
f()
==>
This is a funciton

(3)

def curve_pre():
    a = 25
    def curve(x):
        return a*x*x
    return curve    
f = curve_pre()
print(f(2))
==>
100

上面的例子很容易理解,但如果,在调用curve前,再给a重新赋值,a=10,最终结果会等于100还是40?

def curve_pre():
    a = 25
    def curve(x):
        return a*x*x
    return curve
a = 10
f = curve_pre()
print(f(2))
==>
100

最终结果等于100,而不是40
这就体现了闭包的作用,不会被外部变量赋值影响;闭包=函数+环境变量
环境变量一定要在函数的外部,但是还不能是全局变量(这是一个很重要的特点)

print(f.__closure__)
print(f.__closure__[0].cell_contents)
==>
#上方的return curve语句,返回的不仅仅是def curve,同时把环境变量a=25也返回了
(,)
25

(4)看看下边这个例子能不能理解

def f1():
    a = 10
    def f2():
        a = 20      
        print(a)    #a此时被python认为是一个局部变量,不会影响到外部的变量
    print(a)
    f2()
    print(a)

f1()
==>
10    
20
10

3.闭包怎么用

假设一个人在x轴上行走,初始位置x=0,第一次走了2步,result=2;第二次走了3步,result=5;第三次走了6步,result=11,这个需求怎么实现?
(1)非闭包方式1

origin = 0
def go(step):
    new_pos = origin + step
    origin = new_pos
    return origin
print(go(2))
print(go(3))
print(go(6))
==>
Traceback (most recent call last):
  File ".\c14.py", line 37, in 
    print(go(2))
  File ".\c14.py", line 34, in go
    new_pos = origin + step
UnboundLocalError: local variable 'origin' referenced before assignment
#因为origin在=前出现了,python就认为origin是一个局部变量,而执行到第4行的时候,origin并没有被定义,因此报错

(2)

origin = 0
def go(step):
    new_pos = origin + step
    return new_pos
print(go(2))
print(go(3))
print(go(6))
==>
2
3
6
# 并没有输出想要的结果,每次调用origin都为0

(3)

origin = 0
def go(step):
    global origin
    new_pos = origin + step
    origin = new_pos
    return new_pos
print(go(2))
print(go(3))
print(go(6))
==>
2
5
11
# 实现了功能,但是有一个弊端,全局变量origin,很容易在另一个函数或者过程中被赋上新值,这样go的返回值就可能发生改变,下面来看用闭包如何实现

(4)

origin = 0
def factory(pos):
    def go(step):
        new_pos = pos + step
        pos = new_pos
        return new_pos
    return go

tourist = factory(origin)
print(tourist(2))
print(tourist(3))
print(tourist(5))
==>
Traceback (most recent call last):
  File ".\c14.py", line 41, in 
    print(tourist(2))
  File ".\c14.py", line 35, in go
    new_pos = pos + step
UnboundLocalError: local variable 'pos' referenced before assignment
# 报错:局部变量没有被定义

(5)

origin = 0
def factory(pos):
    def go(step):
        nonlocal pos
        new_pos = pos + step
        pos = new_pos
        return new_pos
    return go

tourist = factory(origin)
print(tourist(2))
print(tourist(3))
print(tourist(6))
==>
2
5
11

得到了想要的结果,验证一下每一步全局变量origin有没有被改变

origin = 0
def factory(pos):
    def go(step):
        nonlocal pos
        new_pos = pos + step
        pos = new_pos
        return new_pos
    return go

tourist = factory(origin)
print(tourist(2))
print(origin)
print(tourist(3))
print(origin)
print(tourist(6))
print(origin)
==>
2
0
5
0
11
0    #全局变量origin一直没有被改变

再来看看函数tourist的环境变量是不是一直在变化

origin = 0
def factory(pos):
    def go(step):
        nonlocal pos
        new_pos = pos + step
        pos = new_pos
        return new_pos
    return go

tourist = factory(origin)
print(tourist(2))
print(tourist.__closure__[0].cell_contents)
print(tourist(3))
print(tourist.__closure__[0].cell_contents)
print(tourist(6))
print(tourist.__closure__[0].cell_contents)
==>
2
2
5
5
11
11   

环境变量的状态得到了保存,因此能得到想要的结果
这个环境变量是要常驻内存的,使用时要注意;
除了使用闭包,也可以用面向对象的办法,用类变量来保存环境变量的中间状态

你可能感兴趣的:(Python学习(十五)--闭包的用法)