3 solutions to createCounter()
Topic source请教一下: 第一种方法的关键在于定义了列表型变量li,在子程序counter中就可以对其进行修改操作。 为什么把li定义成普通变量时,在不用nolocal声明的情况下,子程序中不能对其进行修改操作呢?
我的理解是这样的:
def createCounter1():
li = [0]
def counter():
li[0] += 1
return li[0]
return counter
createCounter()
函数返回的是一个函数counter()
,这个函数没别的作用就是修改并返回createCounter()
里面的li指向的<font color = 'red'>可变参数</font>对象(感觉就像指针一样)。
每次调用createCounter()
,就会在内存中新建一个li
变量,CounterA
接收了返回的函数counter()
,每次调用CounterA()
都对第一次调用createCounter()
时生成的li
指向的可变对象做了一次修改,因此不断调用会返回 1 2 3 4 5
同理第二次调用createCounter()
,它又在内存中新建了一个li(new)
,这时li(new) = [0]
,因此此时再调用仍为 1 2 3 4
其实也可以将li作为counter()
的默认参数,即
def createCounter1():
def counter(li = [0]):
li[0] += 1
return li[0]
return counter
这样估计还记得默认参数坑的大家可能更好理解一点(笑),不记得的请看函数->函数的参数
因为返回给CounterB
是一个新的函数,因此有其新的li(new)
但是不能作为createCounter()
的默认参数
这个时候无论调用几次createCounter()
由于函数默认参数在一开始就构造好了并不会新建li
,这个时候就算CounterB
接收也只会返回 6 7 8 9
@用户7041329703 我差不多懂你的意思,我也不知道我理解的对不对, 你可以试着打印count(), 结果会出现3个结果 都指向那个g(),我想这应该是三个参数不同的f()函数,所以在下面用f1,f2,f3分别指向这三个对象.
一点理解
首先counterA
指向createCounter()
的时候,赋值已经完成,也就是:
counterA = createCounter():
n = [0]
def counter():
....
return counter
然鹅,counterA
只知道返回的counter
是个函数,至于里面是什么,不知道;
直到counterA()
的时候,才会执行返回的counter
函数:
def counter():
nonlocal n
n[0] += 1
return n[0]
执行counter()
的时候发现,n
是在外部定义的:nonlocal n
,这时候的 n
就变成了自由变量:闭包引用的外部作用域的变量
,而一个变量变成自由变量时,它的作用域会发生改变:自由变量随闭包而存活
。
每次执行counterA()
的时候,其实counter()
函数就执行两句代码:
n[0] +=1
return n[0]
而自由变量n
的生命周期和闭包一致,多次调用counterA()
会操作同一个n
,所以 n[0]
会不停的叠加;
至于为什么 n
是一个 list
而不是一个 num
,是因为在闭包内操作自由变量是有规则的:
- 如果自由变量是可变对象,那么闭包可以更新自由变量;比如 list、dict、set...;
请注意: tuple 是不可变对象
; - 如果自由变量是不可变对象,那么闭包则不可以更新自由变量;比如 num、str、tuple...
我和用户7041329703有着类似的问题,就是作者给出的样例代码之中count函数返回的是一个list,fs,但是执行结果里面我们看到的f1(), f2(), f3()却都是数,这里让人感到很疑惑。
你解释的很好!学明白了才能讲得简单,很佩服 f1=createCounter f1 Out[99]: <function __main__.createCounter()> f1=createCounter() f1 Out[101]: <function __main__.createCounter.<locals>.counter()> f1() Out[102]: 1 加了()后才是执行这个函数
回复下 小甜甜郑林200 的疑问
在定义countA时, counterA = createCounter() 不就是理解为countA指向了counterA = createCounter()?
counterA = createCounter时才是将counterA指向了createCounter counterA = createCounter()是将counterA指向了createCounter函数的返回结果,也就是counter函数
把代码放到编译器里step run一下就懂了 在def的时候,函数主体并不运行,直到调用了该函数的时候该函数才运行。 也就是只有在counterA=creatcounter1()的时候才运行了li=[0] 在接下来调用函数counterA()的时候,counterA()将直接指向creatcounter1()的返回值,也就是counter。 所以每次调用counterA()的时候,会直接运行def counter()的过程,而不是运行def creatcounter1。
来人喂公子吃饼干
为什么这里给li作为列表,而不能作为数值变量?