Recursive call causing IndexError in Class function - python

The goal of my program is to have the computer ask a user questions, and return some specifications for a computer that would fit their needs. Right now I am working on QuestionAsker, which is responsible for, as the class name suggests, asking the user questions. I've been hung up on the 4th line of AskQuestion() function. Before I tell you the problem, take a look at the code:
from question import Question
class QuestionAsker():
questions = [
Question("At minimum, what should your game be running on?", ["Low", "Medium", "Ultra"]),
Question("On a scale of 1-3, how much flair do you want on your computer?", ["Low", "Medium", "Ultra"]),
Question("Money doesn't grow on trees. How much money is in your budget?", ["$500", "$1000", "$2000+"]),
]
index = 0
def AskQuestion(self):
userInputForQuestion = raw_input(self.questions[self.index].question + " ")
if userInputForQuestion not in self.questions[self.index].answers:
print("Try again.")
self.AskQuestion()
self.questions[self.index].selectedAnswer = userInputForQuestion
self.index += 1;
def resetIndex(self):
self.index = 0
def ReadQuestions(self):
pass
I was testing this code by calling AskQuestion a couple of times (to loop through all the questions), and to make sure this code was tippy top, I supplied multiple answers that returned "Try again," as it should. The issue is that if I supplied more than 1 wrong answer to a question, but then if I answered correctly after multiple wrong answers, I would get the following error message:
IndexError: list index out of range
I immediately suspected the [self.index] of the self.questions[self.index], so I began printing out the index to the console. I thought that the issue was that the AskQuestion was magically incrementing the self.index, on the final line of the AskQuestion function, but no. It kept printing a consistent number, in the case of the first question, 0!
I'm at my wits end here, and the other questions I've seen on this haven't served much help. Hopefully you guys can help, thanks!

Please take care that in your function body, when a wrong answer is given, the function does not end. It makes a recursive call. When that call ends, the index is still incremented. So the wrong answers still mess the index up.
You should end the function after the wrong call is made, for what you want to happen.
if userInputForQuestion not in self.questions[self.index].answers:
print("Try again.")
self.AskQuestion()
return None
or use else.
if userInputForQuestion not in self.questions[self.index].answers:
print("Try again.")
self.AskQuestion()
else:
self.questions[self.index].selectedAnswer = userInputForQuestion
self.index += 1;
Also please note that using recursion in such a way is not very common. Which made you make the mistake anyway.

Related

I just started programming and am having problem with both of these while loops

I've just starting a programming course and im making an adventure game.
I've gotten up to this "path" where the user must choose between the market or the blacksmith.
If they choose either they have two choices and if they dont say either choice I want to re-ask and go through the loop again. currently if I type everything in first try it works but if I fail to provide a valid answer it prompts to re-enter then doesn't go through the loop again.
How do I write this to take my:
else:
print ("Choice must be (Sword / Platemail)...")
answer = input ("").lower().strip()
and
else:
print ("Choice must be (Bread / Cabbage)...")
answer = input("").lower().strip()
return to the top of the loop with the "answer" in order to have the if and elif statments do what they need to do?
Any help recommendations would be amazing please keep in mind im brand new to programming.
Picture of code in question
The pattern is like this:
while True:
answer = input("Choice?").lower().strip()
if answer in ('sword','platemail'):
break
print("Choice must be Sword or Platemail")
For cleaner code, you might want to put this into a function, where you pass the possible answers and return the validated one.

Passing variables between functions in Python, then back to menu

Slowly progressing with my learning with Python and would love a little hand with some code I've tried to create.
I previously had this program running with Global Variables to get a proof of concept to learn about passing variables between functions. Fully worked fine. However, rather than running the function and returning to the menu, it will just stop where I return the value and not progress back to the main menu I created. It is at the point of "return AirportDetailsGlobal".
I'm sure its a simple one, and as said - still learning!
Really appreciate any help on this!
Full code is on pastebin for further reference - pastebin 89VqfwFV
print("\nEnter airport code for overseas")
osCode = input()
airports = airData
for line in airports:
if osCode in line:
print (osCode, "Found\n\n")
print("Airport Name:",line[1])
OverseaCodeGlobal = osCode
x = int(line[2])
AirDataGlobal = x #changed here
return AirportDetailsGlobal
break
else:
print('Incorrect Choice')
menu()
menu()
If you do a return then your code goes back to where it was called from. If it wasn't called from anywhere (ie. you just ran that script directly) then calling return is in most ways equivalent to calling sys.exit(), ie. the program terminates. It'll never hit your break, leave the loop, or hit your call to menu().
Also, your indentation as given there isn't right, the else is at the same level as the for, not the if. I don't think that's the problem but you might hit it next. ;-)

How can I figure out how to get the user to reply to the question in this python code?

I am struggling with how to put the percentages for the input questions as mentioned in calculatedNumber=score(lab_percent) if that's even correct at all, or is there another string I should be looking at to catch the right percentage for the questions?
Here's my run
def score(earnedPercent, overallWeight):
scorePercent = earnedPercent // 100
scorePercent = overallWeight // 100
scorePercent = scorePercent % 7
return scorePercent
def grade(gradePercent):
if gradePercent >=90 and gradePercent <= 100:
letterGrade="A"
elif gradePercent >=75 and gradePercent <= 90:
letterGrade="B"
elif gradePercent >= 60 and gradePercent <= 75:
letterGrade = "C"
elif gradePercent >= 50 and gradePercent <= 60:
letterGrade = "D"
elif gradePercent < 50:
letterGrade = "F"
return letterGrade
lab_percent=int(input("What is your lab percent so far (-1 if no labs yet)?: "))
calculatedNumber = score(lab_percent)
assignment_percent=int(input("What is your assignment percent so far (-1 if no assignment yet?: "))
calculatedNumber = score(assignment_percent)
exam_percent=int(input("What is your exam percent so far (-1 if no exams yet)?: "))
calculatedNumber = score(exam_percent)
earnedPercent = gradePercent
earnedPercent = earnedPercent.upper()
message='If things keep going the way they are, you should get a ' + earnedPercent + 'in the course, which is a.'
print(message)
This is too long for a comment, so I'll point out a few things and give some next steps to help you solve your issue and improve your question on this site for now and the future:
First there are a few errors in your code:
return scorePercent should be indented to be part of the score function.
score requires a second argument overallWeight but you do not pass this in when you call it
In score you assign the value of scorePercent three times - only the last performed assignment will define what scorePercent is.
grade is never called - possibly part of the issue
You will need to fix these errors, then consider more what exactly you are doing in calculating the score. If you are a beginner I would recommend using a text editor or IDE that has IntelliSense so it can warn you about these errors and help you solve them as you go.
Then once you are getting errors out of the way, you can use StackOverflow to ask more specific questions such as "How do I format this output to have a %" for example. The code you posted does not run, and there is no specific question so it is harder for the community to help you solve your problem.
If you fix those errors and edit your post to have a more specific question I will edit my answer to better help as well. Also keep in mind that it can be helpful to use a debugger or just print out values throughout your code to track what things are and see if they are coming out as what you'd expect.
Two arguments in function
When you define a function as you have here, you've set it to take two inputs already: def score(earnedPercent, overallWeight): takes earnedPercent and overallWeight as inputs. So when you call (use) score it needs these two values. Going to where you call it:
calculatedNumber = score(lab_percent)
You only give it one of the parameters, you need to give it another. Say I had lab_weight = 0.2 then I could say:
lab_score = score(lab_percent, lab_weight)
Then you can use this lab_scoreelsewhere.
Scope
For grade, you don't want to delete it - the problem is you just aren't using it. Later in your code you say earnedPercent = gradePercent but gradePercent is only defined in the scope of your grade function. You need to think of each function as its own black box, you can only give it inputs and get out whatever it returns.
I really suggest using some sort of editor with Python IntelliSense so that these errors are pointed out for you as you write code. It also might be helpful for you to look at a tutorial like this on scope so you can get a more in depth explanation of this as it is really really important when you're learning to code.
Incremental Building
Also, consider doing things piece by piece. You have a decent amount of code here but there are errors throughout and its going to take time to track all of them down. For example, write your score function give it an input and print the result. If it's what you expect, then do it again and assign it to a variable, print. Then do it with another variable (you tend to overwrite your variables a lot so this will help you resolve that). Do it step by step and learn through the process and you'll find you can rebuild this code and get it running while also getting more practice and better understanding.

Try except recursion or while loop?

I am doing a python course where they suggested a try and except block in a while loop in order to keep asking for input until the condition is satisfied. Intuitively I feel it is shorter to just call the function again in the "except" block like this:
def exceptiontest():
try:
print(int(input("number 1: "))+int(input("number 2:")))
except:
print("a mistake happened")
exceptiontest()
exceptiontest()
When asking on the forum on the course I got the reply that it is not the same. I am a bit confused now. Anyone that can clarify for me? Thanks in advance!
Calling the function in the except will eventually raise a RecursionError: maximum recursion depth exceeded error if you keep entering bad inputs. Generally must humans won't be entering that many bad data to hit the error before they give up, but you are unnecessarily putting function calls on a stack.
A while loop is better since it's one function call, waiting for a valid input. IT doesn't waste any more resources than it needs.
while loop, for two reasons
it's clearer to read: while not success, try again
recursion is not free. It leaves the previous function stack open. it could run out of memory (probably won't, in this case, but in principle, avoid it)
Another reason to use the while loop which has not yet been mentioned is that you could leverage the assignment expressions coming with Python 3.8.
The function add encapsulates getting two numbers and trying to add them.
def add():
'try to add two numbers from user input, return None on failure'
x = input('number 1: ')
y = input('number 2: ')
try:
return float(x) + float(y)
except TypeError, ValueError:
return None
The following while loop runs as long as there is no result.
while (result := add()) is None:
print('you made a mistake, make sure to input two numbers!')
# use result

How can I make this dictionary object keep changing under the while statement?

Originally the question was meant to be solved using recursion, but anyway, I just decided to use the while statement.
If recursion is necessary, please let me know.
This is just a part of my code, but I guess the problem is that the dictionaries employee_state and visitor_state keep changing but the changes are not applied to the while statement.
How can I fix that?
Plus, if you need more of my codes to suggest an answer, just comment me.
def order_process(self):
employee_state={}
visitor_state={}
for employee in self.employees:
employee_state[employee]=employee.state
for visitor in self.visitors:
visitor_state[visitor]=visitor.state
while WAITING in employee_state.values() and ARRIVAL in visitor_state.values():
print(employee_state)
print(visitor_state)
for visitor in self.visitors:
for employee in self.employees:
if employee.state == WAITING:
if visitor.state == ARRIVAL:
employee.cook(visitor)
employee_state[employee] = employee.state
visitor_state[visitor] = visitor.state
A quick and easy fix would be to change the while statement to simply be while True: and then at the end of the while block, add the following conditional
if WAITING not in employee_state.values() and ARRIVAL not in visitor_state.values():
break

Categories