jcm420
jcm420

Reputation: 21

How to use lists in recursive functions Python?

I'm writing a function that recursively traverses the file system, and returns a list of all files with the .txt extension.

The pass_test_func parameter is just a function that can be run and checked (i.e. is the file greater than 100 bytes, etc) - The nothing function (set as its default), simply returns its argument.

My implementation:

def visit(dname, pass_test_func=nothing):           
    directory = os.listdir(dname)                   
    byte_list = []
    for file in directory:
        file_dir = os.path.join(dname, file)
        if os.path.isfile(file_dir) and file_dir.lower().endswith('.txt'):
            size = os.path.getsize(file_dir)
            if pass_test_func(size):
                byte_list.append(str(size) + ' ' + file_dir)
        elif os.path.isdir(file_dir):
            visit(file_dir, pass_test_func)
    return byte_list

My problem is that when I recursively call visit in the following lines

elif os.path.isdir(file_dir):
                visit(file_dir, pass_test_func)

the byte_list is cleared to empty again. I understand why this is happening, but have no idea how I would fix it. The list has to be defined within the definition of visit, so whenever I use recursion it will always be reset no matter what right? Maybe some other data structure is better suited, like a tuple or dictionary?

Upvotes: 1

Views: 194

Answers (2)

Blorgbeard
Blorgbeard

Reputation: 103447

Your function returns byte_list, so just append the returned value when you make your recursive call, instead of throwing it away as you currently do:

elif os.path.isdir(file_dir):    
    byte_list += visit(file_dir, pass_test_func)

Upvotes: 3

ShadowRanger
ShadowRanger

Reputation: 155363

Add an optional argument that can be used in the recursive case:

# Using * makes byte_list keyword-only, so it can't be passed by normal callers by accident
def visit(dname, pass_test_func=nothing, *, byte_list=None):           
    directory = os.listdir(dname)           

    # When not passed explicitly, initialize as empty list
    if byte_list is None:
        byte_list = []
    for file in directory:
        file_dir = os.path.join(dname, file)
        if os.path.isfile(file_dir) and file_dir.lower().endswith('.txt'):
            size = os.path.getsize(file_dir)
            if pass_test_func(size):
                byte_list.append(str(size) + ' ' + file_dir)
        elif os.path.isdir(file_dir):
            # Pass explicitly to recursive call
            visit(file_dir, pass_test_func, byte_list=byte_list)
    return byte_list

As an alternative, as suggested by Blorgbeard, since you return byte_list, use that for your visit calls, changing only a single line in your original code:

        visit(file_dir, pass_test_func)

to:

        byte_list += visit(file_dir, pass_test_func)

This creates additional temporary lists, but that's usually not a big deal.

Upvotes: 2

Related Questions