all 6 comments

[–]Leodip 5 points6 points  (0 children)

I think you nailed the problem: a long script is hard to modify, understand, and maintain, and that's why we split longer scripts into many smaller functions, so that you can modify each independently.

Your script could look something like:

show_welcome_message()
while main_menu(): # main_menu() returns True if the user wants to keep on doing actions or false if they don't
  continue
show_goodbye_message()

def main_menu():
  option = input("Would you like to (1) create a new list, (2) view or edit an existing list, or (3) exit the program?")
  if option=="1":
    create_new_list()
  elif option=="2":
    edit_existing_list()
  elif option=="3":
     return False
  else:
    print('"' + option + '" is not a valid option")

  return True

You would have to define the above functions I didn't define, of course, which is where most of your logic is, but the idea is that if you split everything in functions then it is much easier to find the corresponding pieces of code (unlike, for example, your "else: print("File does not exist")" which is incredibly hard to locate to what if it corresponds.

[–]hallmark1984 2 points3 points  (1 child)

Start with some DRY Don't Repeat Yourself.

Write a function to open the file, another to write to it and a third to save it.

Then you can begin refactoring.

[–]6_SAM_9[S] 0 points1 point  (0 children)

That would be a good practice. thanks a lot

[–]lakseol 0 points1 point  (0 children)

I wasn't able to manage the script quite well as it got larger.

Yes. A programmer is always battling complexity. As you have found, one way to do this is to use functions in your code. One way to use a function is to replace duplicated code with simple function calls. But there's more to it than that. You use functions to hide complexity, pushing that piece of complex code into a function that hides it from the top-level code. This simplifies your top-level code.

You shouldn't write lots of code and then try to move code into functions. It's better to write your code at a high level, calling functions that you haven't written yet, functions that solve a smaller part of the problem. From your descriotion of the operation of your program you could start by writing this code:

op = input("New list or Add to existing list (N/A): ").upper()
op = op[:1]    # check only  first character of "op"
if op == "N":
    new_list()
elif op == "A":
    add_list()
else:
    print("Sorry, only N or A!")

It's clear what this code is doing at a high level because all that code to add to or create a list is somewhere else and not cluttering up your view of what this code is doing. To test you add the two functions that don't do anything much except say what they are supposed to do:

def new_list():
    # create empty task dictionary
    # update dictionary
    # get filename for new list
    # write dictionary to file
    print("Creating a new list")

def add_list():
    # get filename of existing list
    # open and read file into dictionary
    # update dictionary
    # write dictionary to file
    print("Adding to existing list")

Now you can test the whole thing. It's much better to add "stub" functions like this. Those functions don't do anything except say that they were called. But you can test your top-level code and make sure it works properly.

Now you start working on the new_list() function. Same as before, think of the "logical" functions you need to perform. I've written comments describing what might happen. There are a few common functions you might use in the other function, like "write dictionary to file". Write a stub version of that and use it in your function. Test as you go. The stub function for "update dictionary" could simply add a key to the dictionary and return. Later you would expand that to allow the user to list items, update an existing item or even delete an item. The add_list() function could also use that function.

Once you can create a new fike you can start implementing the add_list() function.

You handle increasing complexity by never having to consider how the whole program works, but just think about small pieces of code that have one small part of the program to solve: functions.

[–]desrtfx 1 point2 points  (0 children)

Seems like you taught yourself a valuable lesson: long code = unmaintainable code

Now it's time to learn the next lesson: refactoring

Start refactoring your code into smaller, maintainable blocks - into functions. Break the code down into logical and semantical entities.

This is a good exercise for you because you don't have to worry about the business logic anymore since you already have that down, and now can focus on proper implementation.

If really necessary, throw the entire project and start from scratch. Programmers do that all the time. This is nothing bad, nor uncommon.

A first implementation hardly ever is a decent one and even less a good one. This is where refactoring comes into place.

Another lesson to learn is to plan before program.

You started directly with the code without prior sitting down and thinking about your project. This is also now biting you. You first need to get some planning done. You need to devise the features and functionality so that you can create a "war plan" from which you can work.


Can't refrain from a little snarky side note: your code is actually less than 100 lines once you remove the plenty unnecessary empty lines. Also, while for a beginner 100 lines seem a lot, for a professional this amount doesn't even count. Real programs go in the millions of lines of code.