鱼C论坛

 找回密码
 立即注册
查看: 3490|回复: 6

[已解决]python3 的闭包问题

[复制链接]
发表于 2018-11-4 14:47:11 | 显示全部楼层 |阅读模式
12鱼币
  1. def count():
  2.     fs = []
  3.     for i in range(1, 4):
  4.         def f():
  5.             return i * i
  6.         fs.append(f)
  7.     return fs
复制代码



问题出现在使用这个函数的过程中:

有以下三种方式进行引用:

方式1,发生报错

  1. >>> f1 = count()
  2. >>> f1()
  3. Traceback (most recent call last):
  4.   File "<pyshell#1>", line 1, in <module>
  5.     f1()
  6. TypeError: 'list' object is not callable
复制代码


方式2,发生报错:

  1. >>> f1, f2 = count()
  2. Traceback (most recent call last):
  3.   File "<pyshell#2>", line 1, in <module>
  4.     f1, f2 = count()
  5. ValueError: too many values to unpack (expected 2)
复制代码


方式3,正常:

  1. >>> f3, f4, f5 = count()
  2. >>> f3()
  3. 9
复制代码



如上,问题是:

1.为什么方式1在引用的时候会报错?
2.方式2为什么在创建的时候会报错,我是没看懂报错信息
3.方式3怎么就能正常运行了?
最佳答案
2018-11-4 14:47:12
本帖最后由 丨游戏灬需要 于 2018-11-19 23:13 编辑

1 ,f1 = count() ,但def count 的返回值是fs (是列表) ,所以报错说'列表对象
不能用"()"来"引爆"' #由于函数要加了"()"才能触发效果 ,所以我暂且称"()"为
"引爆"

2 ,报错说'太多的值以至于不能解压/分配' ,这和 a ,b =[1 ,1 ,2] ,是一种情况,
也就是说 在count()函数里 ,fs的列表里的值的个数超过了2

3 ,模拟 conut()的结果 ,在count里 ,
#里面未'引爆'的f用lambda来模拟
fs应该 = [lambda :1 *1  ,lambda :2 *2 ,lambda : 3*3]

更正一下 ,
fs储存的原来是叫做f这个函数的引用 ,所以当for循环完后 f函数的效果是:'lambda : 3*3'
实质上 ,因为函数中叫做 fs的列表 添加的是没有'引爆'的 f 函数 ,所以是 f 这个函数的引用 ,如果是'引爆'了的 ,
就变成了添加 独一的 f函数(不会因为 def f():… 而改变内部运行) (与 类对象 相似 )
也就是说 fs = [lambda : 3*3 ,lambda : 3*3 ,lambda : 3*3 ]
f3 ,f4 ,f5 分别等于fs里的 'lambda : 3*3'

所以f3'引爆'后等于 运行了return3*3

补充:
如果要用闭包实现 fs = [lambda :1 *1  ,lambda :2 *2 ,lambda : 3*3] (里面的lambda同样只是代指效果不是真实lambda)的话 ,代码应该这样改:
1 :fs.append(f) 中的 f 必须要'引爆'(成为单独的个体) ,
2 为了列表里的对象可以'引爆' , f的返回值必须是函数(def 或 lambda) ,
3 '个体'要保存有具体的值 ,而不是对外部的引用
其中第二点和第三点的结合 应该 才是闭包? 也就是说 f才是闭包函数 count不是…

例如:
def count():
    fs = []
    for i in range(1, 4):
        def f():                  
            arg =i                 #这句是为了让 arg 变为 f函数内部的值 ,而不是默认用 i的引用 or 如下
            def f2():                     #指 内部arg变量的值 等于 外部 i的值 (i 得是单个值(或独立的类实例) ,而不能是序列(类对象) ,否则arg也只能是个引用(会影响其他的引用))
                return arg * arg
            return f2              
        fs.append(f())
    return fs

f1 ,f2 ,f3 = count()  #f1() = 1 ,f2() = 4 ,f3() = 9


最佳答案

查看完整内容

1 ,f1 = count() ,但def count 的返回值是fs (是列表) ,所以报错说'列表对象 不能用"()"来"引爆"' #由于函数要加了"()"才能触发效果 ,所以我暂且称"()"为 "引爆" 2 ,报错说'太多的值以至于不能解压/分配' ,这和 a ,b =[1 ,1 ,2] ,是一种情况, 也就是说 在count()函数里 ,fs的列表里的值的个数超过了2 3 ,模拟 conut()的结果 ,在count里 , #里面未'引爆'的f用lambda来模拟 fs应该 = [lambda :1 *1 ,lambda :2 *2 ,lambd ...
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-11-4 14:47:12 | 显示全部楼层    本楼为最佳答案   
本帖最后由 丨游戏灬需要 于 2018-11-19 23:13 编辑

1 ,f1 = count() ,但def count 的返回值是fs (是列表) ,所以报错说'列表对象
不能用"()"来"引爆"' #由于函数要加了"()"才能触发效果 ,所以我暂且称"()"为
"引爆"

2 ,报错说'太多的值以至于不能解压/分配' ,这和 a ,b =[1 ,1 ,2] ,是一种情况,
也就是说 在count()函数里 ,fs的列表里的值的个数超过了2

3 ,模拟 conut()的结果 ,在count里 ,
#里面未'引爆'的f用lambda来模拟
fs应该 = [lambda :1 *1  ,lambda :2 *2 ,lambda : 3*3]

更正一下 ,
fs储存的原来是叫做f这个函数的引用 ,所以当for循环完后 f函数的效果是:'lambda : 3*3'
实质上 ,因为函数中叫做 fs的列表 添加的是没有'引爆'的 f 函数 ,所以是 f 这个函数的引用 ,如果是'引爆'了的 ,
就变成了添加 独一的 f函数(不会因为 def f():… 而改变内部运行) (与 类对象 相似 )
也就是说 fs = [lambda : 3*3 ,lambda : 3*3 ,lambda : 3*3 ]
f3 ,f4 ,f5 分别等于fs里的 'lambda : 3*3'

所以f3'引爆'后等于 运行了return3*3

补充:
如果要用闭包实现 fs = [lambda :1 *1  ,lambda :2 *2 ,lambda : 3*3] (里面的lambda同样只是代指效果不是真实lambda)的话 ,代码应该这样改:
1 :fs.append(f) 中的 f 必须要'引爆'(成为单独的个体) ,
2 为了列表里的对象可以'引爆' , f的返回值必须是函数(def 或 lambda) ,
3 '个体'要保存有具体的值 ,而不是对外部的引用
其中第二点和第三点的结合 应该 才是闭包? 也就是说 f才是闭包函数 count不是…

例如:
def count():
    fs = []
    for i in range(1, 4):
        def f():                  
            arg =i                 #这句是为了让 arg 变为 f函数内部的值 ,而不是默认用 i的引用 or 如下
            def f2():                     #指 内部arg变量的值 等于 外部 i的值 (i 得是单个值(或独立的类实例) ,而不能是序列(类对象) ,否则arg也只能是个引用(会影响其他的引用))
                return arg * arg
            return f2              
        fs.append(f())
    return fs

f1 ,f2 ,f3 = count()  #f1() = 1 ,f2() = 4 ,f3() = 9


小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-11-4 15:01:11 | 显示全部楼层
方式1返回的是列表,列表能加括号引用么,,不能
方式2列表3个元素,2个元素不能拆包
方式3因为数目一致,所以可以
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-11-4 17:20:52 | 显示全部楼层
楼上正解
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-11-15 10:21:36 | 显示全部楼层
塔利班 发表于 2018-11-4 15:01
方式1返回的是列表,列表能加括号引用么,,不能
方式2列表3个元素,2个元素不能拆包
方式3因为数目一致 ...

根据程序的情况,为什么不是 1,4,9的列表,而是9,9,9的列表呢?没有看懂运行的机理!
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-11-15 10:29:17 | 显示全部楼层
添加的是函数地址,当调用参数i的时候已经是i=3
如果添加的是f(),实时调用i返回就是1,4,9了
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-11-19 09:50:20 | 显示全部楼层
丨游戏灬需要 发表于 2018-11-17 18:38
1 ,f1 = count() ,但def count 的返回值是fs (是列表) ,所以报错说'列表对象
不能用"()"来"引爆"' #由于函 ...

涉及到本质了,这是python 基础部分最难的地方了。有点像c 里面的指针
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-23 01:56

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表