简体   繁体   中英

Python variable loses value after for loop in recursive function

Is this a Python bug? Variable loses value after for loop in a recursive function. This is test code. I am really parsing XML.

def findversion(aNode, aList, aFlag):
    print "FindVersion ", aNode[0:1]
    print "Findversion ", aFlag
    if aNode[1].find('Software') != -1:
        aFlag = 1
        aList.append(aNode[1])
    if aFlag == 1 and aNode[0] == 'b':
        aList.append(aNode[1])
    print "Before for ", aFlag
    for elem in aNode[2:]:
        print "After for ", aFlag
        findversion(elem,aList,aFlag)

node = ['td', 'Software version']
node2 = ['b', '1.2.3.4' ]
node3 = [ 'td', ' ', node2 ]
node4 = [ 'tr', ' ', node, node3 ]
print node4

myList = list()
myInt = 0
findversion(node4,myList,myInt)
print "Main ",myList

In the program output below, I would always expect the Before for output to be identical to the After for output.

Program output:

['tr', ' ', ['td', 'Software version'], ['td', ' ', ['b', '1.2.3.4']]]
FindVersion  ['tr']
Findversion  0
Before for  0
After for  0
FindVersion  ['td']
Findversion  0

Before for  1
After for  0

FindVersion  ['td']
Findversion  0
Before for  0
After for  0
FindVersion  ['b']
Findversion  0
Before for  0
Main  ['Software version']

Python version:

Python 2.7.3 (default, Dec 18 2012, 13:50:09)
[GCC 4.5.3] on cygwin
Type "help", "copyright", "credits" or "license" for more information.

The confusing output is because the After for 0 output comes from a different recursive call of the function (not the same one as the Before for 0 output just above it).

Here is a version of your function with some extra information to track the depth of the recursive calls:

def findversion(aNode, aList, aFlag, i=1):
    print "FindVersion ", aNode[0:1], 'call:', i
    print "Findversion ", aFlag, 'call:', i
    if aNode[1].find('Software') != -1:
        aFlag = 1
        aList.append(aNode[1])
    if aFlag == 1 and aNode[0] == 'b':
        aList.append(aNode[1])
    print "Before for ", aFlag, 'call:', i
    for elem in aNode[2:]:
        print "After for ", aFlag, 'call:', i
        findversion(elem,aList,aFlag,i+1)

And here is the new output, which shows what I'm talking about:

FindVersion  ['tr'] call: 1
Findversion  0 call: 1
Before for  0 call: 1
After for  0 call: 1
FindVersion  ['td'] call: 2
Findversion  0 call: 2
Before for  1 call: 2         # this is from the recursive call
After for  0 call: 1          # this is from the original call
FindVersion  ['td'] call: 2
Findversion  0 call: 2
Before for  0 call: 2
After for  0 call: 2
FindVersion  ['b'] call: 3
Findversion  0 call: 3
Before for  0 call: 3
Main  ['Software version']

This After is from the enclosing findversion call:

...
print Before for 0
start iterating over aNode
   first td:
     print After for  0
     call findversion
       print FindVersion  ['td']
       print Findversion  0
       find Software, set aFlag = 1
       print Before for 1            <---
       start iterating over aNode
       it's empty
   second td:
     print After for  0              <---
     ...

It is not a bug. The variable aFlag is only local to the specific function call, because it is passed by value. When your program prints 'Before for 1' it never enters the for loop, because aNode[2:] is empty (aNode only has two elements at that time). Thus, it never prints any 'After for' but returns immediately.

The output will be more clear if you put the print statement 'After for' actually after the for loop instead of inside the for loop. Then the output will be consistent.

print "Before for ", aFlag
for elem in aNode[2:]:    
    findversion(elem,aList,aFlag)
print "After for ", aFlag

So in order to fix my program, I need to return the flag.

def findversion(aNode, aList, aFlag):
    print "FindVersion ", aNode[0:1]
    print "Findversion ", aFlag
    if aNode[1].find('Software') != -1:
        aFlag = 1
        aList.append(aNode[1])
    if aFlag == 1 and aNode[0] == 'b':
        aList.append(aNode[1])
        aFlag = 0
    print "Before for ", aFlag
    for elem in aNode[2:]:
        print "After for ", aFlag
        aFlag = findversion(elem,aList,aFlag)
    return aFlag

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM