Issue: Recursion, restarting the entire questionnaire again
You have so many lines in that loop over questions that you might not see the recursion:
def questionnaire(df, questions = questions):
for i in range(0, len(questions)): #len(questions)-1):
# your code omitted for clarity
else:
print("Please type yes or no.")
questionnaire(df, questions = questions) # RECURSION! Starting the questionnaire over again
Let's clean the scene with some refactoring first. Then we can try to fix the issue. Instead of recursion we need a loop.
Refactor: Extract functions to have the loop short and clean
First let us add some comments that explain the abstractions (what your code is intended to do):
def questionnaire(df, questions = questions):
# ask all questions, one at a time
for i in range(0, len(questions)): # for each question
print(list(questions.keys())[i]) # ask current question
ans = str(input()) # get user input as answer
if ans.lower() == 'yes':
# verify if given answer (title-cased YES) was expected
if list(questions.values())[i][1] == "Yes":
df = df[df.Section.isin(list(questions.values())[i][0]) == False]
elif ans.lower() == 'no':
# verify if given answer (title-cased NO) was expected
if list(questions.values())[i][1] == "No":
df = df[df.Section.isin(list(questions.values())[i][0]) == False]
else:
# if given answer is invalid (not in set of expected answers), ask again
print("Please type yes or no.")
questionnaire(df, questions = questions)
return df
Using the comments we can see similarities and refactor this.
Also taking some improvements:
- assume
questions
is a Python dict of key question
mapped to value answer_options
.
Instead of iterating with for-i and then indexing you can use for-each like for question, answer_tuple in questions_dict.items()
then replace list(questions.keys())[i]
to question
and list(questions.values())[i]
to answer_tuple
.
- instead
print(question); ans = input()
you can directly prompt input(question)
def ask(question):
# ask current question
return str(input(question)) # get user input as answer
def store_answer(df, expected)
return df[df.Section.isin(expected) == False]
def collect_answer(df, question, answer_tuple):
ans = ask(question)
while ans.lower() not in {'yes', 'no'}:
print("Invalid answer. Please type either yes or no.")
ans = ask(question)
# valid answer to store
if ans.title() == answer_tuple[1]: # if correct answer given
return store_answer(df, answer_tuple[0])
return df
def questionnaire(df, questions_dict = questions):
# ask all questions, one at a time
for question, answer_tuple in questions_dict.items(): # for each dict entry (question with answer_tuple)
df = collect_answer(df, question, answer_tuple)
return df
Fixed by Pattern: Loop until valid input
Instead of the recursion with questionnaire(df, questions)
we just ask for the current question again in a loop until valid input was given:
ans = ask(question)
while ans.lower() not in {'yes', 'no'}:
print("Invalid answer. Please type either yes or no.")
ans = ask(question)
# valid answer to store
If no valid answer was given, then it only asks the current question again ... as long as the answer is not in the set of valid options {'yes', 'no'}
.
As soon as this while
loop exits, we know the answer is valid and can continue.