2

Suppose I have the following methods:

def read(file: str) -> List[str]:
    temp = []
    with open(file) as f_obj:
        for line in f_obj:
            temp.append(line)
    return temp


def append_items_to_list(folders : List[str], file: str):
    with open(file) as f_obj:
        for line in f_obj:
            folders.append(line)


def main():

    source: str = "source.txt"

    directories : List[str] = read(file=source)
    print(len(directories))     

    folderList : List[str] = []
    append_items_to_list(folders=folderList, file=source)
    print(len(folderList))

if __name__ == '__main__':
    main()

Both methods read from a file, and appends items to a List. The first method creates a temporary List and returns it, while the second accepts a List as a parameter, and appends to it.

Is there a guideline that states which one is preferable? Are there any underlying problems I don't see that I should be concerned about with either one?

I guess you would have to look at why you need to construct a new List, in this case, I don't need to construct a new List, I can just use the second method.

Clarification:

It seems the general answer is to return a new List, but I've narrowed down my problem a little more.

def do_work(source: Path, destination: Path):
    # do some work with the folders.

def create_list_of_empty_folders(folders : List[str], folder: Path):
    # Check if folder is empty, if so, append it to the List.

def cleanup(folders_to_remove: List[Path]):
    # Remove all folders in the List from the HD.


def main():

    portableHD = Path("Path\\to\\HD")
    USB = Path("Path\\to\\USB")

    empty_folders : List[Path] = []

    for folder in portableHD.iterdir():
        do_work(source=folder, destination=USB)
        create_list_of_empty_folders(folders=empty_folders, folder=folder)

     # Outside of the for loop we can safely remove the empty folders.
    cleanup(folders_to_remove=empty_folders)

if __name__ == '__main__':
    main()

In this context, I have no choice but to pass a List and a folder to my function. Obviously, I could write out the code in the for loop without the use of a function, but let's assume I want to write clean maintainable code.

In my first example, I don't really need to use function, I can read and append the data to a List directly in the main method:

def main():

    file: str = "source.txt"

    folders : List[str] = []

    with open(file) as f_obj:
        for line in f_obj:
            folders.append(line)

if __name__ == '__main__':
    main()

But let's assume more work needs to be done with each of these folders, so I refactor my code and create smaller methods, why is returning a temporary List from a function more desirable?

Does your function really have to know that the goal of the caller is to append ?

Why can't my function know the caller wants to append to a List? After all I did break down the code into smaller functions, one of which appends to a List. Even if I were to rename my method to append(folders : List[str], file: str), the function parameters indicate what I want to append.

  • Even better: return a generator. If I want to, I can pay the cost to materialize it into a list. Or maybe I just need the first 3 elements, so I only want to pay the price for those. – Alexander - Reinstate Monica Oct 22 '19 at 19:57
  • @Alexander - I updated my question with a little more clarification. – user347963 Oct 23 '19 at 2:34
2

There is no universal guideline like that.

If we ignore language specific aspects such as language idioms, language capabilities, performance or programming paradigm (i.e. functional orientation) there are a few rules of thumb left.

Considering the broadest reuse possibility of the function you want to implement, depending on the semantic of your operation:

  • is it about modifying a list owned by someone else ? In this case you’d go for passing the list as input/output argument.
  • is it about enriching or transforming a list owned by someone else but where the modification is not necessarily the goal to be achieved ? In this case, pass the list as input argument and return a new result list.
  • is it about producing a new list ? just return the output list without getting an input list.

In your example, it’s about producing a new list of files in a folder. Does your function really have to know that the goal of the caller is to append ? Or could it work esually well without considering the ownership of the list ? So return a new list and let the caller decide what is to be done.

Clarification

These are rules of thumb, but for every program you write, the design decisions are yours.

In your specific use case, you decide to break up a large function in smaller ones. This is a design decision. So you are in principle in first case: the list is owned by the caller who is responsible for the overall processing, and your function is only doing a part of it.

There’s nothing wrong with that and you could stop here. Then comes the point about possible reuse. Personally, I can see plenty of uses for a list of folders in other cases also. This is why I would revise my design decision and refactor the function to produce a new list, and let the caller do the append. If you don’t see opportunities of reuse, let it be.

If there is more to to with the list, I would still try to keep it general, and let the caller apply a function to the elements of the list (the function could be to rename, to touch date, to move, to print, ...). But again, maybe the possibilities I see are not relevant in your case. Ultimately the choice is yours. No rule shall dictate you the choice, if you have founded reasons to do otherwise.

  • I updated my question with a little more clarification. – user347963 Oct 23 '19 at 2:34
  • 1
    @ConsoleGeek I’ve added a clarification. Hope this helps :-) – Christophe Oct 23 '19 at 6:32
1

The guideline I'd recommend is:

Don't put unnecessary restrictions on your user.

If you take a list as a parameter, the user has to create a list and pass it in, even if they don't want more items than the items you're supplying. That makes your code more of a hassle to use than it would be otherwise.

  • I updated my question with a little more clarification. – user347963 Oct 23 '19 at 2:34
  • But you would still need to create a List if the function returns a temporary List. – user347963 Oct 23 '19 at 3:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy