views:

141

answers:

5

Here's a function (credit to user Abbot, for providing it in another question)

def traverse(ftp):

    level = {}
    for entry in (path for path in ftp.nlst() if path not in ('.', '..')):
        ftp.cwd(entry)
        level[entry] = traverse(ftp) 
        ftp.cwd('..')
    return level

Here's what I don't understand: When python enters the function it creates an empty dictionary (level). In the for loop, it stores a directory name as a key in the dictionary. as for the that key's value, python enters the function again and searches for a directory and it becomes that key's value.

But, how is the level dictionary remembering the values inside? I mean, shouldn't it be reset/emptied everytime python enters the function?

+1  A: 

level is a local variable. Every "run" of the function has its own variable called level, so the variables don't interfere with each other.

Amnon
+1  A: 

The scope of level is limited to the function only. Even if a function calls itself, it doesn't mean that that function call's internal variables (a different level) is the same as this one's.

cjrh
+1  A: 

variable level exists only in the scope of a function, at the end of function local variables discarded, so for each execution of traverse there will be it's own level dictionary. Nothing will be re-written or over-written.

SilentGhost
So each time the function calls itself, a new level = {} is created, keeping the old one intact with its values? So when it reaches level[entry], how does it add that value to the previous level's key?
lyrae
why the downvote? @lyrae: it's saved in parent level dictionary, when you assign the result of `traverse(ftp)`
SilentGhost
ah ok. im still reading all answers trying to really grasp this. and sorry, someone must've downvoted everyone. ill upvote.
lyrae
+7  A: 

No. Every "instance" of the function has its own copy of level and there are no side effects between the various copies of level.

Take this folder tree:

root
 `-home
    |- lyrae
    |   |- ftp.py
    |   `- http.py
    `- badp

Here's the (simplified) execution flow when you call ftp on root:

  • ftp(root) creates an empty level dictionary
  • ftp(root) enumerates subfolders: (home).
  • ftp(root) picks the first subfolder and changes directory into it.
  • ftp(root) sets level[home] to the result of ftp in the current folder.


  • ftp(home) creates an empty level dictionary
  • ftp(home) enumerates subfolders: (lyrae, badp).
  • ftp(home) picks the first subfolder and changes directory into it.
  • ftp(home) sets level[lyrae] to the result of ftp in the current folder.


  • ftp(lyrae) creates an empty level dictionary
  • ftp(lyrae) enumerates subfolders: ().
  • ftp(lyrae) is out of subfolders to parse and returns level.


  • ftp(home) completes the assignment: levels = {'lyrae': {}}
  • ftp(home) changes to the next folder.
  • ftp(home) sets level[badp] to the result of ftp in the current folder.


  • ftp(badp) creates an empty level dictionary
  • ftp(badp) enumerates subfolders: ().
  • ftp(badp) is out of subfolders to parse and returns level.


  • ftp(home) completes the assignment: levels = {'lyrae': {}, 'badp': {}}
  • ftp(home) is out of subfolders to parse and returns level.


  • ftp(root) completes the assignment: levels = {'home': {'lyrae': {}, 'badp': {}}}
  • ftp(root) is out of subfolders to parse and returns level.
badp
So only when it 'return level' does this returned dict became a value to parent's level[entry], correct?
lyrae
That is correct.
badp
one more question: (assume dirB is inside dirA. dirB1 and dirB2 inside dirB). in the for loop, it grabs the directory listing for dirA(which returns dirB). it then goes inside dirB and finds dirB1 and dirB2. It goes inside dirB1, finds nothing and returns to dirB. It now goes inside dirB2. But how does it know to go inside dirB2? after it scans dirB1 and returns to dirB, the for loop would return dirB1 and dirB2. why doesn't it go into dirB1 again?
lyrae
No.After `ftp(dirB)` is done with `dirB1`, the program returns to the head of the `for` loop to process the next item, which happens to be `dirB2` in your example. You will move out of the `for` loop only when there are no more items to iterate over.
badp
+2  A: 

These other answers don't quite explain enough I think. Each recursive entrance into this function creates a new local level dictionary. But crucially, also returns it. This means that the local version of level from each recursion becomes a dictionary tree of levels. Once the recursion is unrolled you're left with a tree of dictionaries which refer to each other. This means that the local variables that get created don't get garbage collected because there's a reference to the top most level dictionary on the stack that's been returned from the outer most function.

Benj