0

I am trying to count the number of python files and non-python files in a path recursively.

import os
def main():
    #path = input('Enter an existing path to a file or directory: ')
    path ='/Users/ziyuanhan/PycharmProjects/lab6/'
    print(count_file(path, counter={'py':0, 'non_py':0}))

def count_file(path,counter):
    if os.path.isfile(path):
        if path.endswith('.py') :
            counter['py']+=1
            return path, counter
        else:
            counter['non_py']+=1
            return path, counter
    elif os.path.isdir(path):
        for files in os.listdir(path):
            print(files)
            path = os.path.abspath(files)
            print(path)
            count_file(path, counter)
        return path, counter

main()

The few problems I have is

  1. I had trouble in keeping multiple counters in one recursion function.
  2. Also the return I want is a dictionary format, but I can only do it this way because I have to return it with path.
  3. I use print(files) to check if the function is working alright, but it shows a lot more files(the top 7 files) I never seen in my folder, why is this happening?

When print(files)

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 

/Users/ziyuanhan/PycharmProjects/lab7/recursive_dir_traversal.py
.DS_Store
/Users/ziyuanhan/PycharmProjects/lab7/.DS_Store
.idea
/Users/ziyuanhan/PycharmProjects/lab7/.idea
lab7.iml
/Users/ziyuanhan/PycharmProjects/lab7/lab7.iml
misc.xml
/Users/ziyuanhan/PycharmProjects/lab7/misc.xml
modules.xml
/Users/ziyuanhan/PycharmProjects/lab7/modules.xml
workspace.xml
/Users/ziyuanhan/PycharmProjects/lab7/workspace.xml
km_mi_table.py
/Users/ziyuanhan/PycharmProjects/lab7/km_mi_table.py
km_to_miles.py
/Users/ziyuanhan/PycharmProjects/lab7/km_to_miles.py
wordfrequency.py
/Users/ziyuanhan/PycharmProjects/lab7/wordfrequency.py
('/Users/ziyuanhan/PycharmProjects/lab7/wordfrequency.py', {'non_py': 0, 'py': 0})

BTW we have to use recursive function, it is mandatory as the Prof requested.

Byron
  • 43
  • 5

3 Answers3

1

You don't need to iterate directory recursively yourself. You can use os.walk which yields directories, files for you:

You cannot change local variable / argument of caller. How about returns total_python, total_non_python and use in caller like below?

def count_file(path):
    total_python, total_non_python = 0, 0
    for parent, directories, files in os.walk(path):
        for filename in files:
            if filename.lower().endswith('.py'):
                total_python += 1
            else:
                total_non_python += 1
    return total_python, total_non_python

def main():
    path = input('Enter a path to a file or directory: ')
    total_python, total_non_python = count_file(path)
    print(path, total_python, total_non_python)

Alternatively, os.scandir is also available since Python 3.5.

falsetru
  • 357,413
  • 63
  • 732
  • 636
  • 1
    Thanks, os.walk does make the task easier but our prof force us to use recursive function – Byron Nov 19 '16 at 07:44
  • so the function has to have a base case and a recursive case – Byron Nov 19 '16 at 07:44
  • Not quite, it's a pproject – Byron Nov 19 '16 at 07:45
  • @Byron, Okay, then I will give you home hints instead of giving you direct code. (1) You need to call `count_file` recursively if the file is directory; current it ignore if the file is directory. (2) `total_python += 1` does not affect caller's variable. In changes only in the caller. You'd better return the value and make it used outside the caller. – falsetru Nov 19 '16 at 07:47
  • Hi I changed some of my code according to your suggestions, could you have another look at it please? – Byron Nov 20 '16 at 04:57
1

You can pass a dictionary as an argument to the function and change the values of the items in the dictionary.

First intialize the dictionary:

counters = {'py': 0, 'other': 0}

Then modify it inside the recursive function:

counters['py'] += 1

This will work because dictionaries are mutable.

Community
  • 1
  • 1
stenci
  • 8,290
  • 14
  • 64
  • 104
  • Thanks! The dictionary counter works, but I don't know why the counter keeps giving me 0,0 if I input a path to a directory. Can you have a look please? – Byron Nov 20 '16 at 19:31
  • Hi sorry I forgot to update my question, it is in the question now, I changed some code according to your suggestion. There are multiple files in the input path to the directory, but the output is still (0,0) – Byron Nov 20 '16 at 21:12
  • The first error is that `path = os.path.abspath(files)` assumes that `files` is in the current folder, not the folder you are scanning. I think you meant to use `path = os.path.join(path, files)`. Even if you do that it doesn't work, because you are reusing the variable `path`. You could use another variable `path2` on that line and the following 2 and it works well. – stenci Nov 22 '16 at 14:31
  • You should never delete or edit parts of your question that already have an answer. You could add an **EDIT** at the bottom of the question and explain that you are adding that part to the question in reference to an answer or to a comment. Otherwise future readers will not understand why certain answers or comments were given – stenci Nov 22 '16 at 14:33
0

This function takes a pathname and returns (total_python, total_not_python). It calls itself on each entries in directories. This is meant to be as close to the given code as reasonable.

def count_file(path):
    if os.path.isfile(path):
        if path.endswith('.py') :
            return 1, 0
        else:
            return 0, 1
    elif os.path.isdir(path):
        total_python, total_not_python = 0, 0
        for files in os.listdir(path):
            print(files)
            path = os.path.join(path, files)
            subtotal_python, subtotal_python = count_file(path)
            total_python += subtotal_python
            total_not_python += subtotal_not_python
        return total_python, total_not_python
Dan D.
  • 73,243
  • 15
  • 104
  • 123
  • Thanks! But I don't know why the counter keeps giving me 0,0 if I input a path to a directory. – Byron Nov 20 '16 at 19:30