`
lin_llx
  • 浏览: 127579 次
  • 性别: 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)

相关推荐

    二元关系的闭包运算

    二元关系的闭包运算则是研究这些关系性质的重要工具。本主题将深入探讨二元关系闭包运算的理论、计算方法以及如何通过编程实现可视化。 首先,我们需要理解什么是二元关系。在集合A上,一个二元关系R是A的子集,...

    SmallPython

    `function`和`module`对象的内部结构在SmallPython中有详细呈现,包括函数的闭包、装饰器的实现以及模块的导入机制。 五、异常处理与错误报告 Python的异常处理机制使得程序能够优雅地处理错误。在SmallPython中,...

    Python 研究(Dive Into Python) 5.4

    6. **函数式编程**:Python支持函数式编程概念,如高阶函数、闭包、装饰器和生成器。这些工具使代码更简洁、可读性更强。 7. **迭代器和生成器**:Python的迭代器协议允许对象以迭代方式访问。生成器是轻量级的迭代...

    python从入门到精通笔记_python教程_python_

    Python是一种高级编程语言,以其简洁明了的语法和...配合实际的编程练习,你将能够运用Python解决各种实际问题,无论是在学术研究、数据分析还是软件开发领域。所以,准备好你的编辑器,让我们一起探索Python的世界吧!

    python-handbook_handbook_python_ebook_

    通过《Python Handbook》这本电子书,读者将全面地学习Python编程,无论是在学术研究、数据科学,还是Web开发等领域,都能找到实用的指导和参考资料。同时,不断实践和应用所学知识,将有助于成为一位熟练的Python...

    Python-2.0.1 C语言版源码

    8. **函数式编程元素**:Python支持高阶函数、闭包和列表推导等函数式编程特性。C语言源码将揭示这些特性的实现细节。 9. **线程和并发**:Python-2.0.1的源码还包含了对多线程的支持,尽管Python的全局解释器锁...

    简明Python教程(Python3中文版)

    **Python简介** Python是一种高级编程语言,以其...通过系统学习并实践,你将能够熟练掌握Python,为从事机器学习、深度学习等领域的研究打下坚实基础。记得理论结合实践,不断探索和尝试,才能更好地提升编程技能。

    深入Python3 中文版

    书中涵盖了一系列重要的主题,包括但不限于安装过程、编写第一个Python程序、内置数据类型的理解和运用、字符串操作、正则表达式的使用、闭包与生成器的概念、面向对象编程的基础以及高级迭代器等。此外,还介绍了...

    Python程序设计习题答案.rar

    7. **函数式编程**:可能涉及高阶函数、闭包、装饰器等概念,展示Python在函数式编程风格上的特点。 8. **正则表达式**:教授如何使用Python的re模块进行文本匹配和搜索,进行数据清洗和预处理。 9. **网络编程**...

    python源码剖析

    这一过程涉及到了许多Python的核心概念,如作用域规则、闭包、生成器、装饰器等,这些都会通过源码解析的方式进行深入探讨。 此外,Python的类型系统是另一个重要的研究方向。Python是动态类型语言,这意味着变量在...

    面向项目的_Python程序设计_教学实践与研究

    6. **函数式编程**:探讨Python中的高阶函数,如map、filter和reduce,以及lambda表达式和闭包等概念。 7. **Web开发**:如果面向项目,可能会涉及到Django或Flask等Python Web框架的基础知识,如路由、模板渲染、...

    python核心源码-chm文件

    通过深入阅读和研究"py_s.chm",开发者不仅可以掌握Python的基本语法,还能深入理解Python的运行机制,这对于成为一名资深Python开发者至关重要。同时,这个资源也可能包含一些调试技巧、扩展开发以及对Python源码的...

    Python源码剖析

    5. **作用域和命名空间**:Python的局部作用域、全局作用域以及闭包是如何工作的?源码中`PyObject *locals, *globals`的使用解释了这些概念。 6. **异常处理**:Python的异常处理基于try/except/finally结构,源码...

    Python-剑指Offer原书是用C实现的这里本人用Python对其中大部分使用了Python实现

    6. **函数式编程**:Python支持函数式编程风格,如高阶函数、闭包、生成器等,可能会在某些题目中体现。 7. **文件操作**:读写文件、处理日志等实际操作,考察对Python I/O的理解。 8. **网络编程**:Python提供...

    Python源码剖析-深度探索动态语言核心技术(2008)

    7. **生成器与协程**:深入研究Python的生成器(generator)和协程(coroutine),它们在异步编程中的应用,以及如何通过yield关键字实现控制流。 8. **类与继承**:详细剖析Python的面向对象特性,如类的定义、...

    python-2.7.14

    通过研究这些官方文档,开发者不仅可以学习Python 2.7的基本语法和高级特性,还可以深入了解如何编写高效、可维护的Python代码,同时为向Python 3的迁移做好准备。因此,对于任何想在Python 2.7环境下工作的开发者来...

    SICP(python中文带书签)

    总之,《计算机程序的构造与解释》(SICP)是一本值得深入研究的著作,它通过Lisp和Python等语言,向我们展示了计算世界的广阔与深邃。无论你是初学者还是经验丰富的程序员,这本书都能为你提供新的视角和思考方式。...

    Python编程金典

    作为Python学习资料,它适合初学者入门,也适合有经验的开发者深入研究。 在Python的世界里,语法简洁明了,易于上手,但其强大的功能和广泛的应用领域使其成为现代编程语言中的翘楚。Python支持多种编程范式,包括...

    02_Python_高级讲义.zip

    1. **函数式编程**:Python支持函数式编程范式,包括高阶函数、闭包、装饰器和生成器。高阶函数是可以接受函数作为参数或返回函数的函数,例如`map()`和`filter()`。闭包是拥有自由变量的函数,即使在外部作用域已经...

Global site tag (gtag.js) - Google Analytics