Discuss / Python / 3 solutions to createCounter()

3 solutions to createCounter()

Topic source

为什么这里给li作为列表,而不能作为数值变量?

请教一下: 第一种方法的关键在于定义了列表型变量li,在子程序counter中就可以对其进行修改操作。 为什么把li定义成普通变量时,在不用nolocal声明的情况下,子程序中不能对其进行修改操作呢?

沉MOde鱼

#14 Created at ... [Delete] [Delete and Lock User]

我的理解是这样的:

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

Abel_静默

#15 Created at ... [Delete] [Delete and Lock User]

@用户7041329703 我差不多懂你的意思,我也不知道我理解的对不对, 你可以试着打印count(), 结果会出现3个结果 都指向那个g(),我想这应该是三个参数不同的f()函数,所以在下面用f1,f2,f3分别指向这三个对象.

小黑PYozo

#16 Created at ... [Delete] [Delete and Lock User]

我想问下他返回的不是一个list函数吗?那么不应该f1,f2,f3都应该是1,4,9吗?其次是还有为什么一开始的count返回的都是9呀,我不是很懂.

東NANIAN

#17 Created at ... [Delete] [Delete and Lock User]

一点理解

首先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...

Slangevar

#18 Created at ... [Delete] [Delete and Lock User]

我和用户7041329703有着类似的问题,就是作者给出的样例代码之中count函数返回的是一个list,fs,但是执行结果里面我们看到的f1(), f2(), f3()却都是数,这里让人感到很疑惑。

小盆友-z

#19 Created at ... [Delete] [Delete and Lock User]

你解释的很好!学明白了才能讲得简单,很佩服 f1=createCounter f1 Out[99]: <function __main__.createCounter()> f1=createCounter() f1 Out[101]: <function __main__.createCounter.<locals>.counter()> f1() Out[102]: 1 加了()后才是执行这个函数

Lanse_SJie

#20 Created at ... [Delete] [Delete and Lock User]

回复下 小甜甜郑林200 的疑问

在定义countA时, counterA = createCounter() 不就是理解为countA指向了counterA = createCounter()?

counterA = createCounter时才是将counterA指向了createCounter counterA = createCounter()是将counterA指向了createCounter函数的返回结果,也就是counter函数

DDuo先生

#21 Created at ... [Delete] [Delete and Lock User]

把代码放到编译器里step run一下就懂了 在def的时候,函数主体并不运行,直到调用了该函数的时候该函数才运行。 也就是只有在counterA=creatcounter1()的时候才运行了li=[0] 在接下来调用函数counterA()的时候,counterA()将直接指向creatcounter1()的返回值,也就是counter。 所以每次调用counterA()的时候,会直接运行def counter()的过程,而不是运行def creatcounter1。


Reply