函数嵌套(定义时嵌套)
>>> 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一个内函数