3
\$\begingroup\$

I'm currently in the early stages of a Python project, and the program (so far) asks the user what test they would like to take and what difficulty they would like it to be, and I'd like to know if there're any optimisations that I could make.

class NotWork(Exception): pass
class DoWork(Exception): pass


def which_test(real_dif, real_test, give_test):
    if difficulty == real_dif and test == real_test: 
        give_test()

def is_it_correct(variable, subdif1, subdif2, subdif3, message):
    if variable != subdif1 and variable != subdif2 and variable != subdif3:
        print(message)
        raise NotWork
    else:
        raise DoWork


while True:
    try:
        test = str(input("What test do you want to take? Computer Science, History or Music? ").strip().lower())
        is_it_correct(test, "computer science", "history", "music", """\nSorry, you didn’t input valid settings. Please enter (without the quotes) either "Computer Science", "History", or "Music".\n""")
    except NotWork:
        continue
    except DoWork:
        break

while True:
    try:
        difficulty = str(input("Do you want to take the test in easy, medium or hard? ").strip().lower())
        is_it_correct(difficulty, "easy", "medium", "hard", """\nSorry, you didn't input valid settings. Please enter (without the quotes) either "Easy", "Medium", or "Hard".\n""")
    except NotWork:
        continue
    except DoWork:
        break


which_test("easy", "computer science", easy_CS)
which_test("medium", "computer science", medium_CS)
which_test("hard", "computer science", hard_CS)
which_test("easy", "history", easy_history)
which_test("medium", "history", medium_history)
which_test("hard", "history", hard_history)
which_test("easy", "music", easy_music)
which_test("medium", "music", medium_music)
which_test("hard", "music", hard_music)
\$\endgroup\$
6
\$\begingroup\$

Use of Exceptions/Errors

Raising the DoWork error when input validation succeeds seems strange. Errors are usually raised by a function when, for some reason, it could not do what it was supposed to. Raising an error from is_it_correct if the input was not correct seems like fair game - in fact, there is a built-in ValueError that is meant to be raised when a function is called with values that are somehow malformed.

However, since your method is called is_it_correct already, why not just return a boolean that answers the question that is the method name?

Call signature

Right now, is_it_correct takes one argument per valid choice. What if, at some point, you want to add another test to your program? You would have to create a method that takes an additional valid choice as argument. But then, you would run into trouble with the difficulty selection - it still only has two valid choices.

I recommend using an array for the valid choices for extensibility's sake.

Separation of concerns

Right now, the is_it_correct method is in charge of validating the correctness of the input as well as printing out an error message. I recommend moving the error message to the point in your code where other user interaction takes place already - namely close to the input calls.

With all of the above recommendations considered, is_it_correct and its usage could look like this:

def is_it_correct(choice, valid_choices):
    return choice in valid_choices

while True:
    test = input()
    test_choices = ["computer science", "history", "music"]
    if is_it_correct(test, test_choices):
        break
    else:
        print("Sorry, you didn't input valid settings. Available choices: " + ", ".join(test_choices))

Deduplication

You have two while True loops doing something very similar - prompting the user for one of a given set of choices and displaying an error message if he fails to enter a valid choice. Why not wrap that up in a method?

def require_input(prompt, choices):
  # generate a readable list of choices
  choices_string = "{} or {}".format(", ".join(choices[:-1]), choices[-1]).capitalize()

  # build the prompt message dynamically
  msg = "{} {}?\n".format(prompt, choices_string)

  while True:
    user_choice = input(msg)
    if is_it_correct(user_choice, choices)
        return user_choice
    print("Please input either " + choices_string + ".")

tests = ["computer science", "history", "music"]
test = require_input("What test do you want to take?", tests)

difficulty_levels = ["easy", "medium", "hard"]
difficulty = require_input("How difficult do you want the test to be?", difficulty_levels)
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.