`
lin_llx
  • 浏览: 128477 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Python闭包再研究

阅读更多

前两天写了一篇文章,讲了一下Python的闭包。刚好今天又看到一个小问题,和Python闭包有点相关。顺手记录下来。

 

如下一段代码,

 

 

funcs = []
for i in xrange(10):
    def bar(n):
        return n + i
    funcs.append(bar)

print funcs[3](5)

 

这段代码中,我们期望得到的结果是3+5为8。但是实际得到的结果是什么呢?是14。

 

14是怎么来的?

 

反汇编看看:

 

 

  7           0 LOAD_FAST                0 (n)
              3 LOAD_GLOBAL              0 (i)
              6 BINARY_ADD
              7 RETURN_VALUE  

 

注意i是global。

 

得到14的原因就是,funcs[3]这个函数对象获取i的值,是在执行的时候。而i的作用域是global。也就是说,当这个func开始执行的时候,i已经变成9了。那么结果当然等于14了。

 

从这个结果看,以上代码和下面代码效果是等价的。

 

 

funcs = []
for i in xrange(10):
    pass

def bar(n):
    return n + i

funcs.append(bar)#这句重复10遍
print funcs[3](5)

 

很无趣吧。那么考虑一下,如果把i丢到闭包来做会怎样?

 

 

funcs = []
def foo(m):
    for i in xrange(m):
        def bar(n):
            return n + i
        funcs.append(bar)
foo(10)
print funcs[3](5)

 

很遗憾,结果依然是14. 

 

反汇编代码如下:

 

 

  9           0 LOAD_FAST                0 (n)
              3 LOAD_DEREF               0 (i)
              6 BINARY_ADD
              7 RETURN_VALUE 

 

唉,只是傻傻的远程访问而已。

 

“所有的bar代码中,i仅仅只是在closure中的一个引用而已。指向的依然是同一个对象。当这个对象被改变,所有的bar执行的时候获得的值都是修改后的值”。

 

顺手写了一段JavaScript来测试,发现结果是一样的。也是会全局改变。具体代码如下:

 

 

但是用haskell实现了一个,完全符合预期的结果。

 

 

main = do
    let funcs = [(\n -> n + i) | i <- [1..10] ]
    let x : xs = funcs
    return (x 4)
 

返回结果是5。

 

看来Python对FP的支持还是比不上Haskell这种正统的函数式语言。

 

个人觉得如果Python要发展FP的话,可以考虑如下解决方案:区别本地变量和闭包变量。当声明函数的时候将闭包变量拷贝一份到本地,同时保留指向原闭包对象的引用。在搜索名字的时候,始终都是先搜索本地变量,再搜索本地闭包变量副本,再搜索全局变量。当需要修改的时候,如果是本地变量就直接修改。如果是闭包变量的时候,将原来闭包真正指向的对象进行修改,同时覆盖掉本地的闭包副本。

 

这个方法只是暂时的考虑,没有仔细的推敲。乍看之下似乎可以解决问题。

 

不过,Python毕竟是OO的语言。没有Haskell或者Lisp那种天生的作用域的控制能力。唉。那就用OO的方式来搞Python把。

 

1
1
分享到:
评论
7 楼 Arbow 2011-09-06  
实测scala没有这个问题:)

scala> val list = scala.collection.mutable.ArrayBuffer[Int=>Int]()
list: scala.collection.mutable.ArrayBuffer[(Int) => Int] = ArrayBuffer()

scala> Range(0,11).foreach( i => list.append( (n:Int) => n+i ) )

scala> list(3)(5)
res20: Int = 8

scala> list(5)(5)
res21: Int = 10
6 楼 lin_llx 2011-03-03  
QLeelulu 写道
funcs = []

def bar(i):
    return lambda n: n+i

for i in xrange(10):
    funcs.append(bar(i))

print funcs[3](5)

呵呵,你这个写法是可以达到目的,不过你的和我说的有区别,因为在bar里面,i已经不是指向闭包中的变量了,而是单纯的local变量了。
5 楼 QLeelulu 2011-03-02  
funcs = []

def bar(i):
    return lambda n: n+i

for i in xrange(10):
    funcs.append(bar(i))

print funcs[3](5)
4 楼 lin_llx 2010-10-31  
ouchxp 写道
Python本就不是专注于FP的语言.包括Scala等语言在FP应用上都有或多或少的不足.
真正能成为主流项目开发的FP语言目前还寥寥无几.

PS:F#也不错可以看看


能让我感兴趣的FP语言也只剩下Lisp,Haskell和erlang了。f#的话,我倒是更加欣赏ocaml啊。。
3 楼 ouchxp 2010-10-30  
Python本就不是专注于FP的语言.包括Scala等语言在FP应用上都有或多或少的不足.
真正能成为主流项目开发的FP语言目前还寥寥无几.

PS:F#也不错可以看看
2 楼 lin_llx 2010-10-26  
whitesock 写道
funcs = []
for i in xrange(10):
    def bar(n, i=i):
        return n + i
    funcs.append(bar)
print funcs[3](5)


这个我知道。不过这种奇技淫巧只能当作是hack啊。如果当你真正想改变闭包变量的时候就不行了。
1 楼 whitesock 2010-10-26  
funcs = []
for i in xrange(10):
    def bar(n, i=i):
        return n + i
    funcs.append(bar)
print funcs[3](5)

相关推荐

    【AI绘画领域】Craiyon:简化操作与高效生成的大众创意绘画工具综述

    内容概要:本文介绍了AI绘画工具Craiyon,它由Google和Hugging Face研究人员开发,是DALL-E mini的后续版本。Craiyon以其简便的操作、强大的创意激发能力和快速生成图像的特点受到欢迎。用户只需简单输入文字描述,Craiyon即可生成图像,极大地降低了绘画门槛。文章还探讨了Craiyon面临的挑战,包括版权争议、图像质量提升空间和艺术本质的讨论。最后,展望了Craiyon在教育、商业等领域的应用前景,强调其在未来创意和科技融合中的重要性。 适合人群:适合所有对AI绘画感兴趣的用户,尤其是零基础的艺术爱好者、设计师以及需要快速生成创意图像的专业人士。 使用场景及目标:①帮助设计师突破创意瓶颈,快速生成设计草图;②为普通用户提供实现梦想可视化的途径;③在教育领域辅助创意启发课程,提升学生创造力和跨学科学习能力;④在商业领域加快广告设计、游戏开发等项目的创意构思和制作进程。 其他说明:尽管Craiyon存在版权争议、图像质量有待提高等问题,但其简便易用的特点使其成为初学者和创意爱好者的理想选择。未来,随着技术的进步,Craiyon有望克服现有挑战,为用户提供更高质量的服务。

    实训商业源码-智慧农场 1.9.2+农业众筹投资+活动报名+智慧农场拼团 +农场乐园-论文模板.zip

    实训商业源码-智慧农场 1.9.2+农业众筹投资+活动报名+智慧农场拼团 +农场乐园-论文模板.zip

    实训商业源码-图片表情-论文模板.zip

    实训商业源码-图片表情-论文模板.zip

    实训商业源码-微信·小程序模板社交圈-论文模板.zip

    实训商业源码-微信·小程序模板社交圈-论文模板.zip

    实训商业源码-智慧农场小程序1.8.9-论文模板.zip

    实训商业源码-智慧农场小程序1.8.9-论文模板.zip

    MFC自制人脸识别软件(含虹软人脸识别密钥)

    使用MFC自制的人脸识别小软件,能够实现注册登录基础功能。可以在此基础上进一步开发,实现锁屏登录等功能

    实训商业源码-文件快递-论文模板.zip

    实训商业源码-文件快递-论文模板.zip

    基于vue+javaScript实现的订货系统供货商后台系统+源码(毕业设计&课程设计&项目开发)

    基于vue+javaScript实现的订货系统供货商后台系统+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用 基于vue+javaScript实现的订货系统供货商后台系统+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于vue+javaScript实现的订货系统供货商后台系统+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用 基于vue+javaScript实现的订货系统供货商后台系统+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用 基于vue+javaScript实现的订货系统供货商后台系统+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用 基于vue+javaScript实现的订货系统供货商后台系统+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用

    面试题总结一,包括常见面试题

    面试题总结一,包括常见面试题

    PT7C4512-PLL时钟倍频器-中文数据手册.pdf

    PT7C4512-PLL时钟倍频器-中文数据手册

    基于Matlab Simulink的风轮机发电系统多模块建模与仿真研究 建模与仿真

    内容概要:本文详细介绍了使用Matlab 2022a中的Simulink工具对风轮机发电系统进行全面建模与仿真的方法。主要内容涵盖风速模型(包括基本风、阵风、阶跃风和随机风)、风力机模型(涉及叶片、轴承、发电机等组件)以及飞轮储能模块的构建。通过这些模型,可以更真实地模拟风力发电系统的动态行为,帮助研究人员和工程师更好地理解风电系统的性能特点及其优化方向。 适合人群:从事风力发电系统研究的技术人员、高校相关专业师生、对风电系统感兴趣的科研工作者。 使用场景及目标:①用于风电场的设计和优化;②评估不同风速条件下的风力机性能;③研究飞轮储能模块对电力输出稳定性的贡献。 其他说明:文中提供了完整的Simulink工程源文件和参考文献,便于读者进一步学习和应用。

    实训商业源码-微商城-论文模板.zip

    实训商业源码-微商城-论文模板.zip

    2022全新天狼星网络验证系统源码 开源无授权版

    天狼星网络验证,一个可以注入远程分享弹窗,网络验证的工具你还在用本地注册机吗?你想拥有远程分享弹窗的功能吗?即 刻起就摆脱本地,使用网络注册机! 天狼星网络验证破解,支持一键为软件注入弹窗式网络验证,一键为软件注入全屏式网络验证,支持跳加固注入网络验证, 支持远程引流分享弹窗等等,注入后的软件还有独特的签名验证,防二改,防抓包破解 计算机技术发展到今天,很多优秀的软件已经具备了商业价值,很多软件作者希望自己的软件具有注册功能,但是传统的注册 码解决不了多人使用同一个注册码的问题,机器码(本地验证)的方式又比较容易被破解。这些问题都成为大家制作收费软件 的首要问题刀客源码。本系统就是帮助大家解决这一系列问题。如果你想保护自己的知识产权及将自己的知识产权商业化, 你就一定的试试哦。 完全免费使用用户登录、卡密管理等基本功能,无需担心收费功能影响软件使用! 节省软件成本,不需要购置服务器,登录酸酸云科技后台配置自己的应用程序相关设置,客户端直接调用远程webservices就能 实现网络验证! 缩短开发周期不需要过多的繁琐操作,调用酸酸云科技就可以实现验证机制,无需再书写验证用户登录,用户管理,卡密管理等 繁琐的代码,开发软件只要专注程序代码即可! 安全防破解将重要数据存放在服务器中,只有当客户登陆成功后才会返回相应的重要数据,就算被破解,也有盗版弹窗,也是无 法使用! 支持多程序语言开发同时支持多种程序语言,只要程序能够调用DLL的程序语言都可以调用本插件,例如:按键精灵、易语 言、C#、C++等!

    实训商业源码-智慧农场V1.9.0+拼团1.0.1+报名1.0.6+众筹投资1.1.0+农场乐园1.0.2-论文模板.zip

    实训商业源码-智慧农场V1.9.0+拼团1.0.1+报名1.0.6+众筹投资1.1.0+农场乐园1.0.2-论文模板.zip

    stm32工程文件创建完美模版工程文件

    包含常用所需的全部固件,创建工程只需要复制工程,然后写入主程序代码即可

    繁忙铁路干线施工天窗与列车运行图协同优化研究_于婕.pdf_ (1).pdf

    繁忙铁路干线施工天窗与列车运行图协同优化研究_于婕.pdf_ (1)

    ECharts树图-树图5.zip

    图表效果及代码实现讲解链接:https://blog.csdn.net/zhangjiujiu/article/details/148054080

    cherry-MCP相关软件包.zip

    cherry-MCP相关软件包,包括cherry-MCP组件(Windows版)如uv,uvx等等,以及Cherry-Studio、cursor,node的安装包,提供给需要学习cherry-MCP相关知识的同学。

    电力电子领域中逆变器下垂控制技术的实现及其应用 微电网

    内容概要:本文详细介绍了逆变器下垂控制技术在并网和孤岛模式下的具体实现方法。首先解释了下垂控制的基本概念及其重要性,然后通过具体的代码片段展示了不同模式(如并网模式、孤岛模式)下的控制逻辑。对于并网模式,逆变器跟随电网频率,而在孤岛模式下,则依靠下垂控制调节频率和电压。此外,还讨论了并联运行时可能出现的问题以及解决方案,如功率跷跷板现象和相应的滤波处理。最后,文章强调了实际调试过程中的一些经验和技巧,如动态调整下垂系数以应对不同负载情况。 适合人群:从事电力电子、微电网研究的技术人员,尤其是对逆变器下垂控制感兴趣的工程师。 使用场景及目标:适用于需要理解和实现逆变器下垂控制的工程项目,帮助工程师掌握如何在不同环境下配置和优化逆变器性能。 其他说明:文中提供了多个编程语言的实际代码示例,包括Python、C和Matlab,有助于读者更好地理解理论与实践的结合。同时,作者分享了许多来自真实项目的宝贵经验,使读者能够避免常见的错误和技术陷阱。

    白与祭的keil5工作区安装资料

    可以通过这个包让自己的keil5工作区看起来没那么辣眼睛(也许吧)

Global site tag (gtag.js) - Google Analytics