请选择 进入手机版 | 继续访问电脑版
设为首页收藏本站

Crossin的编程教室

 找回密码
 立即加入
查看: 1730|回复: 7

求助

[复制链接]

2

主题

0

好友

20

积分

新手上路

Rank: 1

发表于 2023-7-21 08:48:18 来自手机 |显示全部楼层
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()


>>> f1()
9
>>> f2()
9
>>> f3()
9
为什么不是1,4,9?
回复

使用道具 举报

174

主题

45

好友

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

发表于 2023-7-22 01:31:30 |显示全部楼层
好问题!

这里函数中的变量i是在外部作用域中定义的,对函数而言不是局部变量,所以这里的 i 在实现上传递的是一个地址,他们都是同一个x

你可以在f函数中加个print(id(i)),就会发现值是一样的,因为它们是同一个i
而且你可以在for循环里函数外加上print,会发现函数内的print直到最终调用 f1 的时候才会执行,而并非在循环定义的时候。

参考:https://blog.csdn.net/wmx1117/article/details/106437950
#==== Crossin的编程教室 ====#
微信ID:crossincode
网站:http://crossincode.com
回复

使用道具 举报

1

主题

0

好友

31

积分

新手上路

Rank: 1

发表于 2024-2-9 09:50:06 |显示全部楼层
本帖最后由 哈尔滨小电工 于 2024-2-9 09:53 编辑
crossin先生 发表于 2023-7-22 01:31
好问题!

这里函数中的变量i是在外部作用域中定义的,对函数而言不是局部变量,所以这里的 i 在实现上传递 ...

时间戳:2024.2.9
要得到1,4,9只要做一步小小的变动就搞定了,把代码中的fs.append(f)改成fs.append(f()),输出print(conut()),或者打count()就是一个列表[1 4 9],shell模式输入f1就是1,f2就是4。
分析这个问题俺觉得得先区别两个概念:第一个fs.append(f)和fs.append(f()),是完全不同的两码事,对这个的认知,crossin老师要背锅,因为在编程教室26课和绿色的教材P63教学的“对列表元素的访问”中都没有指出,当列表中的元素为函数时,表达式的写法是不同的,a[1]要改成a[1](),才完成了对元素访问的同时也调用函数得到return的返回值,这个在看了crossin老师分享的这个csdn链接才发现有所不同。
第二个是“函数的定义”与“函数的调用”的区别,当一个函数在定义时,它的定义式中有内部的“局部变量”存在,那这个局部变量就会被设置好,当函数没有局部变量的定义这一操作时,定义完成后直到函数被调用才会将外部变量用到函数内部的表达式中。在这个for循环内部,每一次循环的内容是“定义了一个函数,并将这个函数作为一个元素放进了一个列表中”,当十个函数定义完成后,循环结束,执行下一步也就是return fs操作,返回了一个元素为十个函数的列表,在shell模式里执行f1(),这时才执行了“函数的调用”,那在执行这个函数时,内部没有局部变量,而函数内的表达式会用到变量i,这个变量在顺序运行的前面的程序中有一个外部变量,也就是最后的那个i=3的值,就被函数使用了,于是得到9,在这之前,这些函数一直存放在列表中没有被调用过。
那么为什么改成fs.append(f())就好使了呢,因为在执行这条代码的时候,它表达的物理意义是:“将函数f()的返回值最为一个元素放进列表中”那这一步就有“函数的调用”这一过程,那这一步循环中,这个函数内部也没有局部变量的定义,但他被调用了,他就会使用此时的前面程序中存在的外部变量i=0,于是得到return的返回值1被放进了列表fs[]中,成了第一个元素。以此类推得到一个元素全为数字型数据的列表fs[]。count函数的返回值就能得到元素为[1 4 9]的一个列表。
个人愚见,如有错误,请务必对在下批评指正。
回复

使用道具 举报

174

主题

45

好友

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

发表于 2024-2-10 01:17:53 |显示全部楼层
哈尔滨小电工 发表于 2024-2-9 09:50
时间戳:2024.2.9
要得到1,4,9只要做一步小小的变动就搞定了,把代码中的fs.append(f)改成fs.append(f()) ...

你没发现你这么改了之后就不能调用 f1() f2() f3() 了吗

函数加不加括号是两码事,不存在是函数就要加上()这种说法,加不加括号取决你需要的是一个函数对象,还是函数的调用结果(也就是函数的返回值)
你虽然把代码改得不报错了,但并不是原题要问的原因。原因如我上面的回答,在定义时函数不会执行
你加了括号,自然函数被执行了,得到了返回结果,但你这个元素的类型也完全不一样了
#==== Crossin的编程教室 ====#
微信ID:crossincode
网站:http://crossincode.com
回复

使用道具 举报

1

主题

0

好友

31

积分

新手上路

Rank: 1

发表于 2024-2-11 06:53:57 |显示全部楼层
crossin先生 发表于 2024-2-10 01:17
你没发现你这么改了之后就不能调用 f1() f2() f3() 了吗

函数加不加括号是两码事,不存在是函数就要加上( ...

是这样,元素类型不一样了,我jio得提问者如果也是小白,可能和我一样在初学的时候没有理解“调用函数”和“定义函数”的区别,以及列表元素是函数时,带上括号才能选择元素并调用。
回复

使用道具 举报

174

主题

45

好友

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

发表于 2024-2-11 23:55:47 |显示全部楼层
哈尔滨小电工 发表于 2024-2-11 06:53
是这样,元素类型不一样了,我jio得提问者如果也是小白,可能和我一样在初学的时候没有理解“调用函数” ...

提问者不是小白,或者说他问的不是一个小白问题,是你没理解他问的点在哪里。所以你才会给了一个直接绕开了问题的改法

他想问的点在于,为什么函数里的i不是定义时的值,而全部变成了最终值。
并不是说要得到1,4,9应该怎么写。

并且即使不用到列表,一样可以重现这个情况:
  1. for i in range(1, 4):
  2.     def f():
  3.          return i*i
  4.     if i == 1:
  5.         a = f
  6.     elif i == 2:
  7.         b = f
  8.     elif i == 3:
  9.         c = f
  10. print(a(),b(),c())
复制代码
要就这个问题把结果改成1、4、9的话,更有针对性改法是把其中的f改为
  1. def f(x=i):
  2.              return x*x
复制代码
完全这个代码需涉及到函数定义的执行顺序、局部变量、变量的引用、以及一个叫做“闭包”的概念。





#==== Crossin的编程教室 ====#
微信ID:crossincode
网站:http://crossincode.com
回复

使用道具 举报

174

主题

45

好友

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

发表于 2024-2-11 23:59:47 |显示全部楼层
crossin先生 发表于 2023-7-22 01:31
好问题!

这里函数中的变量i是在外部作用域中定义的,对函数而言不是局部变量,所以这里的 i 在实现上传递 ...

你可以先尝试理解我在一开始给他的这个回答。

如果还不能理解,说明目前对你来说超纲了。先把基础学完再来。或者你愿意现在啃也行,公众号里回复 局部变量,把几篇文章都看懂

如果仅仅是因为函数加没加括号的区别,我不会专门说这是一个“好问题!”
#==== Crossin的编程教室 ====#
微信ID:crossincode
网站:http://crossincode.com
回复

使用道具 举报

1

主题

0

好友

31

积分

新手上路

Rank: 1

发表于 2024-2-14 10:27:50 |显示全部楼层
crossin先生 发表于 2024-2-11 23:55
提问者不是小白,或者说他问的不是一个小白问题,是你没理解他问的点在哪里。所以你才会给了一个直接绕开 ...

啊,我也是这么想的,我看了那个csdn的帖子,我是觉得举个能出149的例子来对比源代码比较解释得清晰一些。带不带()表达的物理意义明显不同,借此来理解定义函数时同时定义参数和没有定义参数的区别。i是9就是因为循环完事了,i=9了之后才调用函数,赋值的就是i=9了,之前定义了多次函数但都没有把外部变量赋值进去。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即加入

QQ|手机版|Archiver|Crossin的编程教室 ( 苏ICP备15063769号  

GMT+8, 2024-4-13 12:15 , Processed in 0.018744 second(s), 22 queries .

Powered by Discuz! X2.5

© 2001-2012 Comsenz Inc.

回顶部