Self Limiting Repition Function - python
I'm writing a program that is basically a study guide/ practice test for the current section of my A&P class (it keeps me more engaged than just rereading notes over and over). The test works without any problems, but I have an issue where some of my questions use an "enterbox" input, I can have the question loop if the answer is incorrect, but I can't get it to break without a correct answer.
I figured out a way to make it work by putting the entire function back into the initial "else" tree, so that right or wrong you advance to the next question but it looks incredibly ugly and I can't believe there isn't a better way.
So my "solution" looks like such:
def question82():
x = "which type of metabolism provides the maximum amount of ATP needed for contraction?"
ques82 = enterbox(msg = x, title = version)
#version is a variable defined earlier
if ques82.lower() in ["aerobic"]:
add() #a function that is explained in the example further below
question83()
else:
loss() #again its a housecleaning function shown below
ques82b = enterbox(msg = x, title = version)
if ques82b.lower() in ["aerobic"]:
add()
question83()
else:
loss()
question83()
Okay so it worked, but using a nested if tree for each "enterbox" question looks kinda sloppy. I'm self taught so it may be the only solution but if there is something better I would love to learn about it.
So here is a complete section from my program:
from easygui import *
import sys
version = 'A&P EXAM 3 REVIEW'
points = 0
def add():
global points
msgbox("Correct", title = version)
points = points + 1
def loss():
global points
msgbox("Try Again", title = version)
points = points - 1
def question81():
x = "What chemical is stored by muscle as a source of readily available energy for muscle contractions"
ques81 = enterbox(msg = x, title = version)
if ques81.lower() in ["creatine"]:
add()
question82()
else:
loss()
question81()
It works as is so any errors from what's provided are probably my fault from copy and pasting.
Also I'm running it in python 2.7rc1 if that helps.
Thanks for any help in advance.
I don't know if there is a way to combine "enterbox" that has a button for "skip" as that would also be a solution.
Consider the following approach:
We define a list of question and answer pairs. We do this in one place so it's easy to maintain and we don't have to search all over the file to make changes or re-use this code for a different questionset.
We create an ask_question function that we can call for all of our questions. This way, if we want to make a change about how we implement our question logic, we only have to make it in one spot (and not in each of the questionXX functions).
We compare user input to our answer using == and not in (in will do something else, not what you expect).
We create an object to keep track of our answer results. Here, it's an instance of ResultsStore, but it can be anything really, let's just try to get away from global variables.
Use a loop when prompting for answers. The loop will repeat if the answer given was incorrect (and if retry_on_fail is False).
Allow for the user to enter some "skip" keyword to skip the question.
Display the results once the "test" is complete. Here, we do that by defining and calling the store.display_results() method.
So, what about:
from easygui import enterbox
question_answer_pairs = [
("1 + 1 = ?", "2"),
("2 * 3 = ?", "6"),
("which type of metabolism provides the maximum amount of ATP needed for contraction?", "aerobic")
]
VERSION = 'A&P EXAM 3 REVIEW'
class ResultStore:
def __init__(self):
self.num_correct = 0
self.num_skipped = 0
self.num_wrong = 0
def show_results(self):
print("Results:")
print(" Correct:", self.num_correct)
print(" Skipped:", self.num_skipped)
print(" Wrong: ", self.num_wrong)
def ask_question(q, a, rs, retry_on_fail=True):
while True:
resp = enterbox(msg=q, title=VERSION)
# Force resp to be a string if nothing is entered (so .lower() doesn't throw)
if resp is None: resp = ''
if resp.lower() == a.lower():
rs.num_correct += 1
return True
if resp.lower() == "skip":
rs.num_skipped += 1
return None
# If we get here, we haven't returned (so the answer was neither correct nor
# "skip"). Increment num_wrong and check whether we should repeat.
rs.num_wrong += 1
if retry_on_fail is False:
return False
# Create a ResultsStore object to keep track of how we did
store = ResultStore()
# Ask questions
for (q,a) in question_answer_pairs:
ask_question(q, a, store)
# Display results (calling the .show_results() method on the ResultsStore object)
store.show_results()
Now, the return value currently doesn't do anything, but it could!
RES_MAP = {
True: "Correct!",
None: "(skipped)",
False: "Incorrect" # Will only be shown if retry_on_fail is False
}
for (q,a) in question_answer_pairs:
res = ask_question(q, a, store)
print(RES_MAP[res])
Quick and dirty solution could be using the default value "skip" for the answer:
def question81():
x = "What chemical is stored by muscle as a source of readily available energy for muscle contractions"
ques81 = enterbox(msg = x, title = version, default = "skip")
if ques81.lower() == 'creatine':
add()
question82()
elif ques81 == 'skip':
# Do something
else:
loss()
question81()
But you should really study the answer given by jedwards. There's a lot to learn about
program design. He's not giving you the fish, he's teaching you to fish.
Related
Function vs if-statement: Function is not working, but the code in the function will work when outside a function?
I was working on building a randomized character generator for Pathfinder 3.5 and got stuck. I am using the Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill): function to populate a randiomized list of skills with their class based points total, class bonus, and point buy. So modelling the action of a player picking skills for their character. As an example below, Wizards. I pick Knowledge_Arcana as a skill and spend one of my skill point pool (Calculated by taking my intelligence modifier +2) on it. So that skill now equals my intelligence modifier(+1 in this case), class skill bonus as a wizard (+3), plus the point I spent(+1) for a total of 5. The problem is while the function prints the correct result of 5, the outstanding variables do not populate with the final total. To continue our example I'd run the function on Knowledge_Arcana, get a +5, and then check the Knowledge_Arcana after the function call and get just +1. Conversely, if I write out the function as just an if statement it works. Example is next to the function for comparison. Does anyone know why Im getting the different result? ## Creating the lists and breaking into two separate sections Int_Mod = 1 Skill_Ranks = 3 Rand_Class = 'Wizard' Knowledge_Arcana = Int_Mod Knowledge_Dungeoneering = Int_Mod Wizard_Class_Top_Skills = ["Knowledge_Arcana"] Wizard_Class_Less_Skills = ["Knowledge_Dungeoneering"] Class_Skill = 3 Important_Skills_Weighted = .6 Less_Important_Skills_Weighted = .4 Important_Skills_Total_Weighted = round(Skill_Ranks*Important_Skills_Weighted) Less_Skill_Total_Weighted = round(Skill_Ranks*Less_Important_Skills_Weighted) Wiz_Draw =['Knowledge_Arcana', 'Knowledge_Dungeoneering'] def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill): if Skill_String in Draw: Skill_List = Skill_List + Class_Skill + Draw.count(Skill_String) print(Skill_String, Skill_List) else: print('Nuts!') ## Function Calls Populate_Skills('Knowledge_Arcana', Wiz_Draw, Knowledge_Arcana, Class_Skill) Populate_Skills('Knowledge_Dungeoneering', Wiz_Draw, Knowledge_Dungeoneering, Class_Skill) print(Knowledge_Arcana,Knowledge_Dungeoneering) Edited to be a MRE, I believe. Sorry folks, Im new.
You are passing in a reference to a list and expect the function to modify it; but you are reassigning the variable inside the function which creates a local variable, which is then lost when the function is exited. You want to manipulate the same variable which the caller passed in, instead. def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill): if Skill_String in Draw: Skill_List.extend(Class_Skill + Draw.count(Skill_String)) print(Skill_String, Skill_List) else: print('Nuts!') Alternatively, have the function return the new value, and mandate for the caller to pick it up and assign it to the variable. def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill): if Skill_String in Draw: Skill_List = Skill_List + Class_Skill + Draw.count(Skill_String) print(Skill_String, Skill_List) else: print('Nuts!') return Skill_List Skill_List = Populate_Skills('Knowledge_Arcana', Wiz_Draw, Knowledge_Arcana, Class_Skill) # etc You should probably also rename your variables (capital letters should be used for classes and globals; regular Python functions and variables should use snake_case) and avoid using global variables at all. The entire program looks like you should probably look into refactoring it into objects, but that's far beyond the scope of what you are asking.
Perhaps I fundamentally misunderstand indentation in python? - Python
This code gives me an indentation error on checks. I get that this happens often, but the instance is in between two for loops that exist because I need to reference two different lists. I do not even have the data set made yet, but it should report that the syntax is correct at least. The code is fairly simple. I want to automate package placement in a building and I want to do so by taking the biggest packages and putting them in place with the least amount of room where it would still fit. All inputs that I used so far are dictionaries because I need to know which shelf I am referring too. I am this close to turning it to lists and being extremely strict about formatting. inv = maxkey["Inventory"] is the line where the mistake happens. I do not know how to fix it. Should I use lists for this project instead? Is there a flaw in the logic? Is there a parentheses I forgot? Please let me know if this is just an oversight on my part. Please contact me for further details. def loadOrder(inProd, units, loc, pref, shelves): items = len(inProd) while items > 0 # What is the biggest package in the list? mxw = 0 # Frontal area trackers BoxId = {} # Identifies what is being selected for p in inProd: if p["Height"]*p["Width"] > mxw: mxw = p["Width"]*p["Height"] BoxId = p else: pass # What is the location with the least amount of space? maxi = 0.001 maxkey = {} for key in loc: if key["Volume Efficiency"] > maxi and key["Width"] > mxw/BoxId["Height"]: maxi = key["Volume Efficiency"] maxkey = key else: pass maxkey["Inventory"].append(BoxId) weight = 0 volTot = 0 usedL = 0 inv = maxkey["Inventory"] for k in inv: weight = k['Weight']+weight vol = k['Height']*k['Width']*k['Depth']+volTot usedL = k['Width']+usedL maxkey["Volume Efficiency"] = volTot/(maxkey['Height']*maxkey['Weight']*maxkey['Depth']) maxkey['Width Remaining'] = usedL maxkey['Capacity Remaining'] = weight del inProd[BoxId] items = len(inProd) return [inProd, units, loc, pref, shelves]
Indentation in a function definition should be like: def function-name(): <some code> <return something> Also, you have missed : after while loop condition. It shoulde be while items > 0:
And you should not mixing the use of tabs and spaces for indentation. The standard way for indentation is 4 spaces. you can see more in PEP 8.
How to print take something out of a list and store it elsewhere
I am making a music key theory test. In order to achieve something else that I won't explain right now here (maybe I will later though if this doesn't work), I need to take the root chord out of the list, store it somewhere else, and call back upon it for the user input when they are asked what key the chords come from. I don't know how to do this, but I am pretty sure it is possible. I would love it if someone could help me. After I have this problem sorted out I will be much further. Before, I was trying to find someway to have the actual variable represent the variable, while representing what the variable contains. So then after printing the randomized chords from the variable, the user can input the key from which the chords come from, which I have as the variable. But I don't think that will work. import random print('Key Theory Werkout') Dmajor = {'D','E-','F⌗-','G','A','B-','C⌗dim'} Gmajor = {'G','A-','B-','C','D','E-','F⌗dim'} Cmajor = {'C','D-','E-','F','G','A-','Bdim'} Fmajor = {'F','G-','A-','Bb','C','D-','Edim'} Bbmajor = {'Bb','C-','D-','Eb','F','G-','Adim'} Ebmajor = {'Eb','F-','G-','Ab','Bb','C-','Ddim'} Abmajor = {'Ab','Bb-','C-','Db','Eb','F-','Gdim'} Dbmajor = {'Db','Eb-','F-','Gb','Ab','Bb-','Cdim'} Cxmajor = {'C⌗','D⌗-','E⌗-','F⌗','G⌗','A⌗-','B⌗dim'} Gbmajor = {'Gb','Ab-','Bb-','Cb','Db','Eb-','Fdim'} Fxmajor = {'F⌗','G⌗-','A⌗-','B','C⌗','D⌗-','E⌗dim'} Bmajor = {'B','C⌗-','D⌗','E','F⌗','G⌗','A⌗dim'} Cbmajor = {'Cb','Db-','Eb-','Fb','Gb','Ab-','B-dim'} Emajor = {'E','F⌗-','G⌗-','A','B','C⌗-','D⌗dim'} Amajor = {'A','B-','C⌗-','D','E','F⌗-','G⌗dim'} questions = [Dmajor, Gmajor, Cmajor, Fmajor, Bbmajor, Ebmajor, Abmajor, Dbmajor, Cxmajor, Gbmajor, Fxmajor, Bmajor, Cbmajor, Emajor, Amajor] print('Difficulty:Easy, Hard') begin = input("Choose Difficulty:") if begin == 'easy': while begin == "easy": q = random.choice(questions) qq = random.sample(list(q), 7) print(qq) answer = input('Please Provide the key:') if answer == q '''HERE IS THE PROBLEM. Lets say the code outputs F, A-, Bb, C, D- for Dmajor. How can I have the user type in Dmajor and have it print correct, or incorrect? I am thinking I will have to put . entire blocks for each question, and then have the easy choose random out of all of those questions and that will be how I have to do it. But maybe there is an easier way. ''' print("correct") I would like it to tell the user if they are correct or wrong, while keeping the randomness of questions, and chords it spits out exactly the way it is. What do I need to do?
Maybe you can try using a dictionary to represent your questions, like this: questions = { 'Dmajor': {'D','E-','F⌗-','G','A','B-','C⌗dim'}, 'Gmajor': {'G','A-','B-','C','D','E-','F⌗dim'}, 'Cmajor': {'C','D-','E-','F','G','A-','Bdim'}, 'Fmajor': {'F','G-','A-','Bb','C','D-','Edim'}, 'Bbmajor': {'Bb','C-','D-','Eb','F','G-','Adim'}, 'Ebmajor': {'Eb','F-','G-','Ab','Bb','C-','Ddim'}, 'Abmajor': {'Ab','Bb-','C-','Db','Eb','F-','Gdim'}, 'Dbmajor': {'Db','Eb-','F-','Gb','Ab','Bb-','Cdim'}, 'Cxmajor': {'C⌗','D⌗-','E⌗-','F⌗','G⌗','A⌗-','B⌗dim'}, 'Gbmajor': {'Gb','Ab-','Bb-','Cb','Db','Eb-','Fdim'}, 'Fxmajor': {'F⌗','G⌗-','A⌗-','B','C⌗','D⌗-','E⌗dim'}, 'Bmajor': {'B','C⌗-','D⌗','E','F⌗','G⌗','A⌗dim'}, 'Cbmajor': {'Cb','Db-','Eb-','Fb','Gb','Ab-','B-dim'}, 'Emajor': {'E','F⌗-','G⌗-','A','B','C⌗-','D⌗dim'}, 'Amajor': {'A','B-','C⌗-','D','E','F⌗-','G⌗dim'}, } Then, you select a random question like this: key = random.choice(list(questions)) set = questions[key] sample = random.sample(set, 7) Now, you only have to check if answer is equal to key.
Lists won't produce result due to it must be a string. 8 Ball Fortune
The problem with my code is that when running it to see if the first word of the question is in the query list. It won't give anything back due to being a int. Can anyone point me towards right direction ? import random response = [ 'Yes, of course!',"Without a doubt,yes.", 'For sure!','Ask me later.', 'I am not sure.', 'I will tell you after my nap.', 'No Way!','I do not think so.', 'The answer is clearly NO.'] query = ['Will','Are','Is','Am','Do','Can','May'] # Global lists ^^^ def main(response,query,ran): # The program that print('Ask a question that can only be answered in yes or no.') question = input() if question(0) in query(): ran(response,query) res = ran(response,query) print(res) again = input('Enter Y or y to play again. Enter N or n to exit.') if again in ['y','Y']: main(lists) else: print('This program will now close.') def ran(response,query): res = random.choice(response) return res main(response,query,ran)
The problem lies here question.index(0) in lists. You are comparing apples with pears. index will yield a number and you are comparing it with the actual definition of the lists function. You might want to rethink what you want to compare. Secondly, you will have extra errors, you are using random.sample in a bad way, you should be doing random.sample(response, 1)[0], I'm presuming you want to pick a random response from that collection. Thirdly, what is the lists() function supposed to do?
Doesn't process the loop
I am trying to learn python and trying to create a simple application where I have it set to read lines from the text file. The first line is the question and second line is answer. Now, I am able to read the question and answer. But the part where I compare user answer with the actual answer, it doesn't perform the actions in the loop even when the answer entered is correct. My code : def populate(): print("**************************************************************************************************************") f=open("d:\\q.txt") questionList=[] b = 1 score=0 start=0 for line in f.read().split("\n"): questionList.append(line) while b<len(questionList): a = questionList[start] print(a) userinput=input("input user") answer=questionList[b] b = b + 2 print(answer) if userinput==answer: score =score+1 print(score) else: break start += 2 I would really appreciate any guidance on this. My q.txt file: 1. Set of instructions that you write to tell a computer what to do. Program 2. A language's set of rules. Syntax 3. Describes the programs that operate the computer. System Software 4.To achieve a working program that accomplishes its intended tasks by removing all syntax and logical errors from the program Debugging 5.a program that creates and names computer memory locations and can hold values, and write a series of steps or operations to manipulate those values Procedural Program 6. The named computer memory locations. Variables 7. The style of typing the first initial of an identifier in lowercase and making the initial of the second word uppercase. -- example -- "payRate" Camel Casing 8. Individual operations within a computer program that are often grouped together into logical units. Methods 9. This is an extension of procedural programming in terms of using variables and methods, but it focuses more on objects. Object Oriented Programming 10. A concrete entity that has behaviors and attributes. Objects
Your code was: always asking the same question: questionList[start] throwing away the value of every replacing every space in answers with nothing, so "System Software" becomes "SystemSoftware" failing to factor in case: need to use .lower() on userinput and answer. Here's a more pythonic implementation: #!/usr/bin/env python3 from itertools import islice # Read in every line and then put alternating lines together in # a list like this [ (q, a), (q, a), ... ] def get_questions(filename): with open(filename) as f: lines = [line.strip() for line in f] number_of_lines = len(lines) if number_of_lines == 0: raise ValueError('No questions in {}'.format(filename)) if number_of_lines % 2 != 0: raise ValueError('Missing answer for last question in {}'.filename) return list(zip(islice(lines, 0, None, 2), islice(lines, 1, None, 2))) def quiz(questions): score = 0 for question, answer in questions: user_answer = input('\n{}\n> '.format(question)) if user_answer.lower() == answer.lower(): print('Correct') score += 1 else: print('Incorrect: {}'.format(answer)) return score questions = get_questions('questions.txt') score = quiz(questions) num_questions = len(questions) print('You scored {}/{}'.format(score, num_questions))