Each loop in Python and Ruby refers to a variable problem (a secret bug?) _python

Source: Internet
Author: User
Tags closure in python


Although I have encountered this problem in Python, it is easier to explain it in Ruby. In Ruby, there are many ways to traverse an array, and the two most commonly used are for and each:


 code as follows:

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

Arr.each {|e|
Puts E
}

For E in Arr
Puts E
End

Usually I prefer the latter, it seems to be nice to write, but the former should be a little bit quicker in terms of efficiency, since the latter is actually called a lambda function for each element during traversal, although it is generally not obvious, but setting the context and invoking the function does have overhead, Especially in dynamic languages (regardless of JIT inline optimization). But this time the problem is not performance. It does, however, have to do with "each to create a new scope for every element and the for is not".





Look at the following code:


 code 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 call distinctions get? Should have guessed it already? respectively, ' a! ' and ' c! ', the latter is ' c! ' because the for does not recreate a scope at every step of the loop, so the closure of the three lambda references the same variable, which was assigned the last time ' C ', causing the Consequences.





The problem is actually a paragraph in a small program I wrote in Python, and the code looks like this:


 code as follows:

For prop in Public_props:
SetAttr (proxy, ' get_%s '%prop, Lambda:self.get_prop (prop))

Where proxy is a proxy object that I provide, exposing some of the exposed properties of self, because to restrict access to non-public properties, I do not want to deposit any references to self in this proxy, otherwise, in Python without access restrictions, through a similar PR Oxy._orig_self.some_private_prop way to access is easy. So finally chose the above approach.





Unfortunately, because, as we just said, the for does not create a scope individually each time, so closure is all referenced to the same variable, causing all the property values to be taken out of the last attribute. See such a strange bug, if it is in C + +, I am sure to suspect the memory or the problem of the pointer. But thought for a long time to finally suddenly realize! But there's no Ruby in Python that's easy to use, and the lambda is pretty chicken, so it's finally solved by defining a local function:


 code 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, let's say that for the previous Ruby example, if you reverse the order of each and for, you get a different result:







 code 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 two of them are ' c! '! This is because the parameters of the block in the implementation of Ruby 1.8 can be assigned to anything, such as a local variable or a global variable, rather than as simple as the parameter of a lambda function in the usual sense. Because the previous for statement creates an e as a local variable in the current scope, each is assigned directly to the local variable so that each reference becomes the same thing, causing a hidden Bug!





Thankfully, this "feature" of block has been removed from Ruby 1.9, and block parameters are only normal parameters, so there is no such problem. Hope 1.9 popularization as soon as possible!


Related Article

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.