函数嵌套(定义时嵌套)

>>> def funcA1():
    def funcA11():
        def funcA111():
            pass        
 

函数调用

>>> funcA1()
>>> funcA11()
Traceback (most recent call last):
  File "<pyshell#202>", line 1, in <module>
    funcA11()
NameError: name 'funcA11' is not defined

 

 

返回函数

 python是一种面向对象语言,所有的内容都是对象,包括函数,所以函数(函数名)可以用来调用和返回

(1)定义函数  def func():

(2) 调用函数  func()

          函数内部需要有__call__()的 method

 (3)函数赋值

         a=funcA1()      # funcA1()的返回值,赋给a

         a=funcA1       #函数名赋给a ,a()也就指向了这个函数的对象

         a()

(4)return函数

         return funcA2()      #返回 函数的返回值

         return funcA2      #返回  函数名

        

>>> def funcB1():        #这个是返回函数的值(返回值)
    def funcB11():
        pass
    return funcB11()

>>> def funcC1(a):      #这个是闭包,这个是返回函数(名)
    def funcB11():
        print(a)
    return funcB11

 

外函数,内函数,闭包closure

 


 

维基百科中关于闭包的概念:
在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。

(1)函数的嵌套: 外函数,内函数

(2) 内函数引用了(相对)外部函数的 局部变量(但不是global 变量)

(3)外部函数,返回了内部函数名

(4)函数没法像类一样调用内部的函数,但是函数形成闭包就可以了

(5) 这个是函数式编程的优点?通过闭包实现了泛函?

          使得多个程序,计算机,之间可以实现分工计算不同的函数和参数,并行工作,最后串联起来

 

对比:普通函数,闭包,类的对比

普通函数--对比如果不用闭包

一个直线函数例子

需要重复传递 k b 这几个比较稳定的量,其实获得Y坐标 只需要提供X坐标即可

>>> def line(k,b,x):
    return k*x+b

>>> line(1,0,5)
5
>>> line(1,0,10)
10
>>> line(1,0,15)
15

如果是用闭包的话,可以只根据内函数的参数改变,外函数的变量(闭包变量)可以保持稳定

但是如果内函数,改了 闭包变量,那么闭包变量 在多次调用时,值会跟着改变,具体见下面的
>>> def line(k,b):
    def line_y(x):
        return k*x+b
    return line_y

>>> line1=line(1,0)
>>> line1(5)
5
>>> line1(10)
10
>>> line1(15)
15

如果用类实现

>>> class Line():
    def __init__(self,k,b):
        self.k=k
        self.b=b
    def line_y(self,x):
        return self.k*x+self.b

    
>>> line1=Line(1,0)
>>> line1.line_y(5)
5
>>> line1.line_y(10)
10

 

外函数,内函数,闭包基础概念

外函数:内部定义了其他函数的函数

内函数:定义在其他函数内部的函数

闭包:内函数 使用 外函数的 局部变量

           外函数 return 内函数的函数名

过程:    外函数被调用时,不光需要传递自己的局部变量,同时将 内部函数名 赋给了 新变量

               新变量名 即 变成了 内函数名 (引用/tag),也即 新变量() 也指向了 内函数

 

>>> def outer(a):
    b=5
    def inner():
        print (a+b)
    return inner

>>> varA=outer(3)
>>> varA()
8
>>> varB=outer(10)
>>> varB()
15
>>> 

为什么这么用?我试验了下,闭包的作用--猜想

(1)因为一个函数内部的 内函数,在外部是无法调用的,连函数名,存储位置都找不到

(2) 想在外部调用,必须让外函数,return内函数,这样外部就可以用到了

(3)另外,这样闭包使用后,实际上 outer(100) 返回的就是 内函数名了

>>> def outer1(a):
    b=5
    def inner1():
        print (a+b)
    return inner1

>>> outer1(100)
<function outer1.<locals>.inner1 at 0x0000000002FF4D90>
>>> inner1    #在外部是无法调用inner1这种内部函数名的
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    inner1
NameError: name 'inner1' is not defined

 

 

 

内函数如何修改外函数的变量?--闭包变量

(1) 因为内函数一般是不能修改外部的变量的,虽然可以随便访问,但不能修改

(2)闭包函数有特殊的修改方法

  在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。

直接修改外函数的局部变量,会报错

>>> def outerA(a):
    b=5
    def innerA():
        b=b+1
        print (a+b)
    return innerA

>>> varB=outerA(1)
>>> varB()
Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    varB()
  File "<pyshell#28>", line 4, in innerA
    b=b+1
UnboundLocalError: local variable 'b' referenced before assignment

 

事先声明了nonlocal 可以修改内函数外部的,外函数的变量
>>> def outerA(a):
    b=5
    def innerA():
        nonlocal b
        b=b+1
        print (a+b)
    return innerA

>>> varC=outerA(1)
>>> varC()
7

 

外函数和内函数,每次都会重新生成和消灭

但是闭包变量,实际上 被保存着,是同一份,下次修改,会在内函数的  nonloacl y的之前基础上修改

>>> def outerC(x):
    y=5
    def innerC(z):
        nonlocal y
        y=y+1
        print (x+y+z)
    return innerC

>>> varC=outerC(10)
>>> varC(100)
116
>>> varC(1000)
1017
>>> varC(1000)
1018
>>> varC(1000)
1019
>>> 

 

原理分析

>>> def line(k,b):
    def line_y(x):
        return k*x+b
    return line_y
 

>>> dir(line)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

>>> dir(line.line_y)               #函数没法像类一样调用内部的函数,但是函数形成闭包就可以了
Traceback (most recent call last):
  File "<pyshell#58>", line 1, in <module>
    dir(line.line_y)
AttributeError: 'function' object has no attribute 'line_y'

>>> line1=line(1,0)
>>> line1(5)
5
>>> dir(line1)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> 

 

 

用处?---现在不懂

1 装饰器

2 面向对象

3 单利模式?

 

 

闭包的几个容易错的地方

(1) 要明白,函数只有1个实例,已经被固化下来!

         函数只是一段可执行代码,编译后就“固化”了,每个函数在内存中只有一份实例,得到函数的入口点便可以执行函数了

(2)闭包每运行1次,每次外函数都会重新调用1次内函数,内函数每次都不一样

          即使调用同样的参数,也是不同的

         如果是普通函数,共享引用了,确实是==的

(3)闭包是那一部分?

         闭包:内函数(包含 闭包变量---运行环境 --且运行环境会被保存下来)

                   但我理解,闭包,是指这个外函数和内函数构成的整体,不知道对不对?

(4)闭包变量是哪个?  -----被内函数引用的都是闭包变量

         外函数:(局部)参数,闭包变量(被内函数引用的,且不能是global)

         内函数:(局部)参数

>>> def outer(a):
    x=10
    def inner(b):
        return a+b
    return inner

>>> outer(1)(2)   #只有a是闭包变量
3
>>> def outer(a):
    x=10
    def inner(b):
        return a+b+x
    return inner

>>> outer(1)(2)     #a是,x也是闭包变量
13

(5)闭包变量怎么修改

        在内函数里,理论上是不能修改 闭包变量的。因为闭包变量属于外函数,

       直接在内函数里修改闭包变量,会造成,没定义这个闭包变量,却先修改了的error

       如果需要修改,则在内函数里,先声明 nonlocal b 这样即可。

>>> def lazy_sum(*args):
    b=1
    def sum():
        nonlocal b
        a=0
        for n in args:
            a=a+n
        b+=1
        return a
    print("b is",b)
    return sum

>>> lazy_sum(1,2,3,4,5)()
b is 1
15
>>> lazy_sum(1,2,3,4,5,6,7)()
b is 1
28
>>> f1=lazy_sum(1,2,3,4,5)
b is 1
>>> f2=lazy_sum(1,2,3,4,5)
b is 1
>>> f1==f2
False
>>> 

#对比,普通函数,共享引用1个函数,是相等的

#但是,重新定义的2个函数,即使看起来一样,也是不同的,不是共享引用,不==

>>> def funB1():
    return 10

>>> funB1()
10
>>> B1=funB1
>>> B1()
10
>>> funB1==B1
True

>>> def funC1():
    return 10

>>> funB1==funC1
False

(6)闭包变量

          会随着闭包变化

#试验成功的例子,如果不赋给变量,每次调用,都是相当于重新计算,所以闭包变量每次都重来

但是,如果闭包赋给了一个变量,连续call两次,就会发现,闭包变量,每次在闭包调用结束后,还保存着没删

>>> def outA():
    a=[]
    def inA():
        a.append(100)
        return a
    return inA

>>> outA()()
[100]
>>> outA()()
[100]

>>> m=outA()
>>> m()
[100]
>>> m()
[100, 100]

 

#试验失败的例子,怎么试验都是重置的,没看见闭包变量保存了,现在还不知道原因是???

>>> def outer(a):
    x=10
    def inner(b):
        nonlocal x
        return a+b+x
    a+=1                   #也试验过x+=1
    return inner

>>> outer(1)(2)
14
>>> outer(1)(2)
14
>>> f1=outer(1)
>>> f1(2)
14
>>> f1(2)
14
>>> 

(7)循环到最后才计算,可能出的BUG

可以看到,inner最后调用时,会把x的变化也算进去

return 的x 也是变化的

>>> def outer(a):
    x=10
    def inner(b):
        nonlocal x
        return a+b+x
    x+=1
    return inner

>>> outer(1)(2)
14

 

#原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量变到循环最终了

#返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量

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

>>> f1,f2,f3=count()
>>> print(f1(),f2(),f3())
9 9 9

#办法就是,这样?没太理解
>>> def count():
    def f(j):
        def g():
            return j*j
        return g
    listA=[]
    for i in range(1,4):
        listA.append(f(i))
    return listA

>>> f1,f2,f3=count()
>>> print(f1(),f2(),f3())
1 4 9
 

# 这个方法为啥也可以???

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

>>> f1,f2,f3=count()
>>> print(f1(),f2(),f3())
1 4 9
>>> 

 

 

(8)只能有一个return

        只能return一个内函数