"Python notes" from a bug code to understand Python's naming Rule

Source: Internet
Author: User

from the description of the Python document about naming and binding, the variable name is bound to a specific object, which, from this point of view, can be understood as a reference in C + +. Consider the following two lines of statements:
A = ' test ' a = ' test_ext '
after line 1th executes, the Python interpreter creates a string-type object ' test ' in memory, which, once created, cannot modify its value . The assignment symbol simply binds the variable name a to the object.
After line 2nd executes, similarly, a string object with a value of ' Test_ext ' is created, and the variable name A is re-bound to the new object. At this point, the ' test ' object cannot be accessed through a.
re-emphasize: The behavior of these two lines of statements is completely different from the behavior in C + +! in C, when an int a = 1 is executed, a memory unit known as a and size sizeof (int) is created (of course, because the compiler makes alinging optimizations to memory, the actual requested memory size may not be more than sizeof (int)). The memory unit holds an int value of 1, and a = 2 executes, a represents the memory unit address unchanged, and its value is updated to 2.

As for the above Python naming rule, I have written a simple note before , and I will not dwell on it.

The above rules are easy to understand, but in actual engineering projects, it is possible to inadvertently introduce bugs to the code by neglecting the " assignment-binding " feature (an experienced C/s code is prone to make this mistake when writing Python code), which, once introduced, Because of its invisibility (because it hides where I think it's most unlikely to go wrong), it's time-consuming to quickly locate in thousands of lines of code. Yes, the bug is there, but can not see exactly what line of code introduced, think about a bit crazy ...

The purpose of this note is to deepen the understanding of this naming rule by a simplified bug code.

directly on the code (including bug):
#!/bin/env python#-*-encoding:utf-8-*-def Test ():    a_dict = {' A1 ': {' s1 ': [' foo ']}, ' A2 ': {' s1 ': [' Bar ']}}    b _dict = {}    print ' begin:a_dict=%s '% (a_dict)    print ' begin:b_dict=%s '% (b_dict) for    K in A_dict.keys (): 
   
    for SK, SV in A_dict[k].items ():            if SK in b_dict:                b_dict[sk].append (SV)                b_dict[sk].append (' addition ')            else:                B_dict[sk] = sv    print ' end:a_dict=%s '% (a_dict)    print ' end:b_dict=%s '% (b_dict)    Return 0if ' __main__ ' = = __name__:    Test ()
   
The above code fragment is intended to construct b_dict through a_dict, specifically, A_dict is a 2-level dict structure, its 2nd-level Dict key as B_dict key, if the a_dict has the same 2-level key, Then their value is merge into a list and then append a ' addition ' element, the final list as the value of b_dict under the 2-level key.
We can think about the execution of the above code and compare it to the actual running result:
begin:a_dict={' A1 ': {' s1 ': [' foo ']}, ' A2 ': {' s1 ': [' Bar ']}}begin:b_dict={}end:a_dict={' A1 ': {' s1 ': [' foo ', [' Bar '], ' ad Dition '}, ' A2 ': {' s1 ': [' Bar ']}}end:b_dict={' s1 ': [' foo ', [' Bar '], ' addition '}
Note In the above results, the value of the a_dict has also been changed, which is obviously not the expected behavior! As you can see, the above code is indeed a bug.
It's okay to have bugs, but the point is, what is the line of the hole that was introduced? (If you do not read the follow-up explanation, you can quickly locate the source of the problem?)

Yes, it is b_dict[sk] = SV (line 15th), its actual behavior is to bind B_dict[sk] to the structure of SV, and the SV is associated with a_dict, so the append operation for B_dict[sk] is equivalent to the " Refer to "Manipulating a_dict content directly!" This behavior is in line with the Python naming rule, just not in line with our expectations ...

The above code also shows that the list returned by Dict's Kesy ()/values ()/items () method has a binding relationship with the dict itself (that is, it does not generate a list through a "deep copy"), and when the returned list is modified, Dict The original content will also be updated.
A more simplified example can be used to verify this conclusion:
>>> a = {' K1 ': [' v1 '], ' K2 ': [' v2 ']}>>> a.items () [(' K2 ', [' V2 ']), (' K1 ', [' V1 '])]  # # Initial, A.items () Content >>> ID (a.items ())  140251308636208>>> x = A.items () >>> ID (x) 140251308636352  # # Because the items () return a new list each time, it is understandable that the ID (x) is inconsistent with the ID (a.items ()) value, if the value is just coincidence >>> x[(' K2 ', [' V2 ']), (' K1 ', [' V1 '])]  >>> ID (x[0][1])  140251308636136>>> ID (a.items () [0][1]) 140251308636136>>> ID (a[' K2 ' ]) 140251308636136  # # Special Note that the ID values of a[' K2 ' and x[0][1] are the same, indicating that they point to the same memory address >>> x[0][1].append (' Test ') >> > a{' K2 ': [' v2 ', ' Test '], ' K1 ': [' v1 ']}  # # After you modify the content, A's content has also been modified!
From the above two-end code example analysis can be seen, the root cause of the bug is:Assignment operations are the process of binding variable names to actual objects, rather than recreating and initializing objects.
Recognizing this, bug fixes are simple. The following is a bug-free code snippet:
#!/bin/env python#-*-encoding:utf-8-*-def Test ():    a_dict = {' A1 ': {' s1 ': [' foo ']}, ' A2 ': {' s1 ': [' Bar ']}}    b _dict = {}    print ' begin:a_dict=%s '% (a_dict)    print ' begin:b_dict=%s '% (b_dict) for    K in A_dict.keys (): 
   
    for SK, SV in A_dict[k].items ():            if SK in b_dict:                b_dict[sk].append (SV)                b_dict[sk].append (' addition ')            else:                B_dict[sk] = list (SV)  # # Note Here the "deep copy" effect is achieved by re-creating the object via list ()    print ' end:a_dict=%s '% (a_dict)    print ' end:b_dict=%s '% (b_dict)    return 0if ' __main__ ' = = __name__:    Test ()
   
The above code only modifies one place (see note) to get the desired result:
begin:a_dict={' A1 ': {' s1 ': [' foo ']}, ' A2 ': {' s1 ': [' Bar ']}}begin:b_dict={}end:a_dict={' A1 ': {' s1 ': [' foo ']}, ' A2 ': {' s1 ' ': [' Bar ']}}end:b_dict={' s1 ': [' foo ', [' Bar '], ' addition '}

Resources
Pythondoc:naming and binding

==================== EOF ====================


"Python notes" from a bug code to understand Python's naming Rule

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.