Variable reference in the each loop in Python and Ruby (a hidden BUG ?)

Source: Internet
Author: User
This article mainly introduces the issue of each circular variables in Python and Ruby, similar to the use of referenced variables in PHP foreach, for more information, see. although I encountered this problem in Python, it is easier to explain it in Ruby. In Ruby, there are many methods to traverse an array. The two most common methods are for and each:

The code is as follows:


Arr = ['A', 'B', 'C']

Arr. each {| e |
Puts e
}

For e in arr
Puts e
End


I usually like the latter because it seems to be nice to write, but in terms of efficiency, the former should be a little faster, because the latter actually calls a lambda function for each element in the traversal process, although it is not obvious in general, it is indeed overhead to set the context and call the function, especially in dynamic languages (without considering JIT inline optimization ). However, this issue is not about performance. However, it is related to "each creates a scope for each element, but for is not.

See the following code:

The code is as follows:


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 # =>?


What do two calls get separately? I already guessed it, right? They are 'a! 'And 'C! ', The latter is 'C! 'Because for does not create a new scope at every step of the loop, the closure of the three lambda references the same variable, this variable was assigned a value of 'C' for the last time, resulting in such a consequence.

The problem actually comes from a section of a small program I wrote in Python. the code is similar to this:

The code is as follows:


For prop in public_props:
Setattr (proxy, 'get _ % s' % prop, lambda: self. get_prop (prop ))


Proxy is a proxy object provided by me. it exposes some public attributes of self, because it is necessary to restrict access to non-public attributes, I don't want to store any reference to self in this proxy. otherwise, in Python that has no access permission restriction, I use a method similar to proxy. _ orig_self.some_private_prop is easy to access. So I finally chose the above method.

Unfortunately, as we mentioned earlier, for does not create a single scope every time, so closure references all of them to the same variable, as a result, all attribute values are obtained as the last attribute. I have seen such a strange bug. if it is in C/C ++, I must doubt the memory or pointer problem. However, after half a day, I finally realized it! However, in Python, there is no Ruby-so convenient each to use, and lambda is also very useful. Therefore, a local function is defined to solve the problem:

The code is as follows:


Def proxy_prop (name ):
Setattr (proxy, 'get _ % s' % prop, lambda: self. get_prop (name)
For prop in public_props:
Proxy_prop (prop)


Finally, I have to say one more word. for the Ruby example, if we reverse the execution order of each and for, we will get different results:

The code is as follows:

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! '


Now both are 'C! ! This is because the block parameters in Ruby 1.8 can assign values to local variables or global variables, rather than the parameters of a lambda function in the general sense. Because the previous for statement creates an e in the current scope as a local variable, each directly assigns a value to this local variable. in this way, every time you reference it, it becomes the same thing, leading to a hidden Bug!

Fortunately, this "feature" of block has been removed in Ruby 1.9. The block parameter can only be a normal parameter, so this problem no longer exists. Hope 1.9 will be popularized as soon as possible!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.