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)
1 Answer 1
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)