`

Python和Ruby中each循环引用变量问题(一个隐秘BUG?)

    博客分类:
  • ruby
阅读更多
这篇文章主要介绍了Python和Ruby中each循环引用变量问题,类似PHP的foreach中使用引用变量的问题,需要的朋友可以参考下
 
 

虽然这个问题我是在 Python 里遇到的,但是用 Ruby 解释起来比较容易一些。在 Ruby 里,遍历一个数组可以有很多种方法,最常用的两种无非是 for 和 each:

复制代码代码如下:

arr = ['a', 'b', 'c']

arr.each { |e|
  puts e
}

for e in arr
  puts e
end

通常我比较喜欢后者,似乎因为写起来比较好看,不过从效率上来说前者应该会稍微快一点,因为后者实际上是在遍历的过程中对每个元素都调用一个 lambda 函数来做的,虽然一般情况下并不明显,不过设置上下文并调用函数确实是有开销的,特别是在动态语言里面(不考虑 JIT 内联优化的话)。不过这次的问题并不是性能。然而确实跟“ each 对每个元素都会新建一个 scope 而 for 则不是”有关。

 

看下面一段代码:

复制代码代码如下:

arr = ['a', 'b', 'c']
h1 = Hash.new
h2 = Hash.new

arr.each { |e|
  h1[e] = lambda { e+'!'}
}

for e in arr
  h2[e] = lambda { e+'!' }
end

h1['a'].call # => ?
h2['a'].call # => ?

两个 call 分别会得到什么?应该已经猜到了吧?分别是 'a!' 和 'c!' ,后者之所以是 'c!' 是因为 for 并没有在循环的每一步都重新创建一个 scope ,因此三个 lambda 的 closure 引用到了同一个变量,而这个变量在最后一次被赋值为 'c' ,所以导致了这样的后果。

 

问题其实出自我在用 Python 写的一个小程序中的一段,代码类似于这样:

复制代码代码如下:

for prop in public_props:
    setattr(proxy, 'get_%s'%prop, lambda: self.get_prop(prop))

其中 proxy 是我提供的一个代理对象,将 self 的一些公开的属性给暴露出去,因为要限制对非 public 的属性的访问,我并不想在这个 proxy 中存放任何到 self 的引用,否则在没有访问权限限制的 Python 里通过类似 proxy._orig_self.some_private_prop 的方式来访问是轻而易举的。所以最后选择了上面那样的做法。

 

不幸的是,由于像刚才所说的那样,for 并没有每次都单独创建 scope ,因此 closure 全部引用到了同一个变量上,导致所有的属性值取出来都是最后一个属性了。看到这样诡异的 bug ,如果是在 C/C++ 里面,我肯定要怀疑是内存或者指针的问题了。不过想了半天才终于恍然大悟!不过 Python 里面没有 Ruby 那么方便的 each 可以用,lambda 用起来也很鸡肋,所以最后通过定义一个局部的函数来解决了:

复制代码代码如下:

def proxy_prop(name):
    setattr(proxy, 'get_%s'%prop, lambda: self.get_prop(name)
for prop in public_props:
    proxy_prop(prop)

最后,还要多嘴一句,对于之前 Ruby 那个例子,如果把 each 和 for 的执行顺序颠倒过来,会得到不同的结果:

 

 

复制代码代码如下:
arr = ['a', 'b', 'c']
h1 = Hash.new
h2 = Hash.new

for e in arr
  h2[e] = lambda { e+'!' }
end

arr.each { |e|
  h1[e] = lambda { e+'!'}
}

h1['a'].call # => 'c!'
h2['a'].call # => 'c!'

现在两个都是 'c!' 了!这是因为 Ruby 1.8 的实现里面 block 的参数可以对局部变量或者全局变量之类的任何东西进行赋值,而不是通常意义上的一个 lambda 函数的参数那么简单。由于前面的 for 语句在当前作用域创建了一个 e 作为局部变量,因此 each 就直接对这个局部变量进行赋值了,这样,每次引用到的又变成了同一个东西,导致了一个隐秘的 Bug !

 

值得庆幸的是,block 的这个“特性”在 Ruby 1.9 中已经被去除了,block 的参数只能是正常参数,所以就不再存在这样的问题了。希望 1.9 尽快普及吧

分享到:
评论

相关推荐

    使用Python Lua和Ruby语言进行游戏编程

    使用Python Lua和Ruby语言进行游戏编程

    关于Python如何避免循环导入问题详解

    module_a.py中定义了一个action_a()函数,该函数引用了module_b.py中的一个attribute,如一个函数或变量 module_b.py中定义了一个action_b()函数,该函数引用了module_a.py中的一个attribute,如一个函数或变量 ...

    从零开始学Python:Python语言中的变量.docx

    从零开始学Python:Python语言中的变量.docx从零开始学Python:Python语言中的变量.docx从零开始学Python:Python语言中的变量.docx从零开始学Python:Python语言中的变量.docx从零开始学Python:Python语言中的变量...

    python类和函数中使用静态变量的方法

    主要介绍了python类和函数中使用静态变量的方法,实例分析了三种常用的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下

    Python程序基础:Python中的变量.pptx

    变量名只能包括字母、数字和下划线,且第一个字符必须是字母或下划线,不能是数字。 str,_str1,str_2 2str,2_str,&123,%lsso,M.Jack,-L2 例如: 第一个单词首字母小写,之后的单词首字母大写,如myName,...

    python中for循环变量作用域及用法详解

    在讲这个话题前,首先我们来看一道题: 代码1: def foo(): return [lambda x: x**i for i in range(1,5,2)] print([f(3) for f in foo()]) 伙伴们,你们认为这里产生的结果是什么呢?我们再来看下这题的变体: ...

    Eric is a full featured Python and Ruby editor and IDE,

    Eric is a full featured Python and Ruby editor and IDE, written in python. It is based on the cross platform Qt gui toolkit, integrating the highly flexible Scintilla editor control. It is designed to...

    Python语言基础:变量.pptx

    任务描述:设计一个小程序,首先了解圆形各参数的基本计算公式,运用本节介绍的操作运算符实现输入、输出圆形的基本参数。 任务分析:输入半径,输出面积及周长 输入面积,输出半径及周长 输入周长,输出半径及...

    Python程序设计:Python常量和变量.pptx

    在本任务中,我们将先学习Python变量、字符串、列表、字典、集合等的使用方法,并穿插相应的案例及分析过程,实行理论讲解和案例分析相结合的方式。 任务 NBA球星信息查询 任务知识点 Python常量和变量 Python数字和...

    Python 如何在字符串中插入变量

    主要介绍了Python 如何在字符串中插入变量,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下

    Python语言基础:局部变量和全局变量.pptx

    定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。 访问范围 局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被...

    python 3.7 静态变量和内部变量的bug(csdn)————程序.pdf

    python 3.7 静态变量和内部变量的bug(csdn)————程序

    基于Python实现全局和局部双变量Moran指数计算

    基于Python实现全局和局部双变量Moran指数计算,输入参数可直接是shapefile文件。

    用Python,Lua和Ruby语言设计游戏

    用Python,Lua和Ruby语言设计游戏,是python lua 和ruby的入门类书籍(英文版)

    python设置环境变量

    使用Python设置环境变量

    Python程序设计-循环结构说课稿.docx

    循环结构作为Python程序设计的三大基本结构之一,有助于学生更好的解决生活中的实际问题,通过这节课的学习,学生会对循环结构有个更深入的了解,并为三种结构的综合学习奠定基础,所以是本单元的重点之一。...

    python 类的私有变量和私有方法.docx

    在Python中可以通过在...这是python的私有变量轧压(这个翻译好拗口),英文是(private name mangling.) **情况就是当变量被标记为私有后,在变量的前端插入类名,再类名前添加一个下划线"_",即形成了_ClassName__变量名.**

    python和ruby,我选谁?

    本文给大家对比了下python和Ruby的异同以及各自的优缺点等,向大家展示了python与Ruby的资源以及学习曲线,非常适合在此两种语言中犹豫不决的小伙伴,希望大家能够喜欢

    python-os.system引用变量写法.pdf

    Python基础 python_os.system引用变量写法

Global site tag (gtag.js) - Google Analytics