Python - class Flashcard - quizzing user in a loop - python

I am trying to create a loop that quizzes the user with flashcards I have created. When the user wants to exit, they should be able to type the letter 'q' and have the number of correct and incorrect answers print. Here is what I have:
class Flashcard(object):
def __init__(self, q, a) :
self.question = q
self.answer = a
def print_question(self) :
print self.question
def quiz_user(self) :
self.print_question()
ans = raw_input("? ")
correct = 0
incorrect = 0
if ans.strip().lower() == self.answer.strip().lower() and ans.strip().lower() != 'q':
print "Good job!"
correct = correct + 1
return True
elif ans.strip().lower() != self.answer.strip().lower() and ans.strip().lower() != 'q':
print "Sorry, the answer was:", self.answer
incorrect = incorrect + 1
return False
elif ans.strip().lower() == 'q':
print "correct:", correct
print "incorrect:", incorrect
import random
cards = [
Flashcard("What is largest country in Africa?", "Algeria"),
Flashcard("What is a group of larks called?", "exaltation")
]
while True :
random.choice(cards).quiz_user()
When I run the code, I get an error saying that the "local variable 'incorrect' referenced before assignment". How can I keep track of the correct and incorrect answers? Should I be returning more than just True and False in quiz_user()? Would it help to make the quiz loop in a new class?

Yeah you need to declare the two variables first, and then pass them in to and get them out each time, so try this:
correct = 0
incorrect = 0
cards = [
Flashcard("What is largest country in Africa?", "Algeria"),
Flashcard("What is a group of larks called?", "exaltation")
]
while True :
result = random.choice(cards).quiz_user()
if result: correct += 1
elif not result: incorrect += 1
else: break
That should allow you to keep using your True False structure, and have the code work. You could also then get rid of your references to correct and incorrect inside your class, and put the print statements in the while loop. Also change this:
elif ans.strip().lower() == 'q':
print "correct:", correct
print "incorrect:", incorrect
to this:
elif ans.strip().lower() == 'q': return None
to have the program stop when q is entered.

Related

Editing a list of dictionaries from a list created from a txt file

I am trying to exclude and remove some dictionaries from a list. I have searched for awhile through the site and haven't found anything specific to this. The list of dictionaries was created from a txt file located: http://s000.tinyupload.com/?file_id=48953557772434487729
I'm trying to sort out and exclude the things I don't need. I thought my syntax was right, but apparently not.
I only included necessary code to cut down on the clutter. I am having problems at the action_genre point and excluding and deleting the dictionaries there. When prompted enter "s" and then "a" to access those two menus.
def load_movies():
global movies_list
movies_list= []
file_ref = open("movies.txt", 'r')
line = file_ref.readline()
for line in file_ref:
line = line.strip()
current = {}
if line == '':
break
movie_data = line.split("\t")
current["title"] = movie_data[0]
current["year"] = movie_data[1]
current["length"] = movie_data[2]
current["rating"] = movie_data[3]
current["action"] = int(movie_data[4][0]) == 1
current["animation"] = int(movie_data[4][1]) == 1
current["comedy"] = int(movie_data[4][2]) == 1
current["drama"] = int(movie_data[4][3]) == 1
current["documentary"] = int(movie_data[4][4]) == 1
current["romance"] = int(movie_data[4][5]) == 1
movies_list.append(current)
del current
file_ref.close()
def menu():
movie_selector =("Movie Selector - Please enter an option below:\nL - List all movies\nY - List all movies by year\n"
"T - Search by title\nS - Search by genre, rating, and maximum length\nQ - Quit the program\nOption:")
movie_selector_input = input(movie_selector).upper()
if movie_selector_input == "L":
list_movies()
if movie_selector_input == "Y":
list_by_year()
if movie_selector_input == "T":
search_by_title()
if movie_selector_input == "S":
search()
if movie_selector_input == "Q":
print("Thanks for using my program! Goodbye.")
exit()
else:
print("Invalid input")
print("Please try again")
print()
return menu()
def search():
genre_input = input("Please make a selection from the following genres.\n(Action(A), Animation(N), Comedy(C), "
"Drama(D), Documentary(O), or Romance(R)):").lower()
if genre_input == 'a':
action_genre()
elif genre_input == 'n':
animation_genre()
elif genre_input == 'c':
comedy_genre()
elif genre_input == 'd:':
drama_genre()
elif genre_input == 'o':
documentary_genre()
elif genre_input == 'r':
romance_genre()
else:
print("Invalid genre")
print()
menu()
#this is where I can't get the syntax to work
def action_genre():
for current in movies_list:
if current["action"] == "False":
del current
break
for i in movies_list:#using this to test output
print(i)
load_movies()
menu()
I'm narrowing down the list by excluding things that don't fit the parameters. In the action_genre function, I'm trying to delete all the dictionaries that don't equal current["action"] == True. I've tried using "True" and "False" as strings, as well as the bools True and False for comparisons, and still an error. Unfortunately, I have to use the Boolean logic per my professors directions.
His e.g.:
Professor's example. Apparently since I'm new I can't embed images. :/
I'm in programming 101, so thank you for your patience as I learn this, and thank you in advance for the help.
Okay, so the issue runs a bit deeper than the if condition being incorrect. In get_action() you effictively do not modify the actual movies_list object, but rather the local variable current, as proven by this simple test:
def action_genre():
for current in movies_list:
print(current)
if not current["action"]:
del current
print(current)
The second print(current) will result in an UnboundLocalError saying that current does not exist anymore, while in movies_list the entry it just deleted continues to exist. But in general, using del in loops indeed causes problems because that's how iterables and del itself behave. I encourage you to read up more on this on other sources or SO if you wish, like here.
Using the answer from the provided link above, we can use list comprehension to filter the movies:
def action_genre():
filtered_movies_list = [movie for movie in movies_list if movie['action']]
print(filtered_movies_list)
This creates a new list (so it does not modify movies_list), which includes all dictionary entries where item['action'] == True.
I hope this helps.
You are trying to compare a string with a boolean. Look at the following:
a= 1==1
print a
True
print type(a)
<class 'bool'> # a is a boolean
b='True' #assign string 'True' to b
print type(b)
<class 'str'>
print a==b #compare boolean True to string 'True'
False
b = True # assign boolean True to b
print a==b #compare boolean True to boolean True
True
so you need if current["action"] == False instead of if current["action"] == "False"

creating new classes in a loop according to user input

So I'm trying to make a prime sieve that will make a class object that stores the prime number up to a number the user gives. But I want to run in a loop where the user can keep creating new objects with lists of prime numbers stored, and give them the option to keep doing that.
If they input the same number twice, I want to just print the previous results which have been stored inside the object created from before, instead of replacing the previous object and running through the computing of setting up a whole new list.
I think the only thing I'm hung up on is how do I just keep creating new class objects over and over and give the objects new names every time instead of what I did the first time, storing the object in the variable 'c'.
If you look at my code, the sieve will work properly, I just don't know how to make an object without storing it in the variable 'c'
history={}
class Eratosthenes(object):
def __init__(self,b):
self.limitn=int(b)+1
self.not_prime = [False]*self.limitn
self.primes = []
def do_primes_sieve(self):
for i in range(2, self.limitn):
if self.not_prime[i]:
continue
for f in xrange(i**2, self.limitn, i):
self.not_prime[f] = True
self.primes.append(i)
def get_primes_til(self):
for i in self.primes:
print (i)
def get_num_of_primes(self):
print (len(self.primes))
a = True
while a:
b = raw_input("How far are you going to search?: ")
if b in history.keys():
c = history[b]
c.get_num_of_primes
else:
c = Eratosthenes(b)
history[b] = c
c.do_primes_sieve()
c.get_primes_til()
d = raw_input("Continue? (type 'yes' or 'no'): ")
if d == "yes":
continue
elif d == "no":
a=False
else:
print "This is a 'yes' or 'no' question. We're done here."
a=False
You could declare some class variable (aka static variable), e.g. a dictionary called history, which will store results:
class Eratosthenes(object):
history = {}
def __init__(self,b):
self.limitn=int(b)+1
self.not_prime = [False]*self.limitn
self.primes = []
def do_primes_sieve(self):
if self.limitn-1 in Eratosthenes.history.keys():
self.primes = Eratosthenes.history[self.limitn-1]
else:
for i in range(2, self.limitn):
if self.not_prime[i]:
continue
for f in xrange(i**2, self.limitn, i):
self.not_prime[f] = True
self.primes.append(i)
Eratosthenes.history[self.limitn-1] = self.primes
Then, you could modify your do_primes_sieve method so that, if a previous answer exists in the history dictionary, the primes for the current instance of Eratosthenes will be fetched from the dictionary.
Per discussion below, an updated answer that treats history as a global:
history = {}
class Eratosthenes(object):
def __init__(self,b):
self.limitn=int(b)+1
self.not_prime = [False]*self.limitn
self.primes = []
def do_primes_sieve(self):
for i in range(2, self.limitn):
if self.not_prime[i]:
continue
for f in range(i**2, self.limitn, i):
self.not_prime[f] = True
self.primes.append(i)
def get_primes_til(self):
for i in self.primes:
print i
def get_num_of_primes(self):
print len(self.primes)
a = True
while a:
b = raw_input("How far are you going to search?: ")
if b in history.keys():
c = history[b]
c.get_num_of_primes()
else:
c = Eratosthenes(b)
history[b] = c
c.do_primes_sieve()
c.get_primes_til()
d = raw_input("Continue? (type 'yes' or 'no'): ")
if d == "yes":
continue
elif d == "no":
a=False
else:
print "This is a 'yes' or 'no' question. We're done here."
a=False

What is wrong with my defintion of the function prompt_int?

I have been trying to program a maths quiz that both works and is as efficient as possible. Looking over my code I saw I had a lot of integer inputs and that lead to me having the program to ask the question/exit the system if the criteria isn't met, so to help me I thought that it would be useful to create a new function. Here is my attempt:
def prompt_int(prompt=''):
while True:
if status == prompt_int(prompt=''):
val = input(prompt)
if val in (1,2):
return int(val)
return true
elif status != prompt_int(prompt=''):
val = input(prompt)
if val in (1,2,3):
return int(val)
return true
else:
print("Not a valid number, please try again")
However, when I try to implement this function around my code it doesn't work properly as it says that status isn't defined however, when I do define status it goes into a recursion loop. How can I fix this problem?
Here is my original code before i try to implement this function:
import sys
import random
def get_bool_input(prompt=''):
while True:
val = input(prompt).lower()
if val == 'yes':
return True
elif val == 'no':
return False
else:
sys.exit("Not a valid input (yes/no is expected) please try again")
status = input("Are you a teacher or student? Press 1 if you are a student or 2 if you are a teacher")# Im tring to apply the new function here and other places that require integer inputs
if status == "1":
score=0
name=input("What is your name?")
print ("Alright",name,"welcome to your maths quiz."
"Remember to round all answer to 5 decimal places.")
level_of_difficulty = int(input(("What level of difficulty are you working at?\n"
"Press 1 for low, 2 for intermediate "
"or 3 for high\n")))
if level_of_difficulty not in (1,2,3):
sys.exit("That is not a valid level of difficulty, please try again")
if level_of_difficulty == 3:
ops = ['+', '-', '*', '/']
else:
ops = ['+', '-', '*']
for question_num in range(1, 11):
if level_of_difficulty == 1:
number_1 = random.randrange(1, 10)
number_2 = random.randrange(1, 10)
else:
number_1 = random.randrange(1, 20)
number_2 = random.randrange(1, 20)
operation = random.choice(ops)
maths = round(eval(str(number_1) + operation + str(number_2)),5)
print('\nQuestion number: {}'.format(question_num))
print ("The question is",number_1,operation,number_2)
answer = float(input("What is your answer: "))
if answer == maths:
print("Correct")
score = score + 1
else:
print ("Incorrect. The actual answer is",maths)
if score >5:
print("Well done you scored",score,"out of 10")
else:
print("Unfortunately you only scored",score,"out of 10. Better luck next time")
class_number = input("Before your score is saved ,are you in class 1, 2 or 3? Press the matching number")
while class_number not in ("1","2","3"):
print("That is not a valid class, unfortunately your score cannot be saved, please try again")
class_number = input("Before your score is saved ,are you in class 1, 2 or 3? Press the matching number")
else:
filename = (class_number + "txt")
with open(filename, 'a') as f:
f.write("\n" + str(name) + " scored " + str(score) + " on difficulty level " + str(level_of_difficulty))
with open(filename, 'a') as f:
f = open(filename, "r")
lines = [line for line in f if line.strip()]
f.close()
lines.sort()
if get_bool_input("Do you wish to view previous results for your class"):
for line in lines:
print (line)
else:
sys.exit("Thanks for taking part in the quiz, your teacher should discuss your score with you later")
if status == "2":
class_number = input("Which classes scores would you like to see? Press 1 for class 1, 2 for class 2 or 3 for class 3")
if class_number not in (1,2,3):
sys.exit("That is not a valid class")
filename = (class_number + "txt")
with open(filename, 'a') as f:
f = open(filename, "r")
lines = [line for line in f if line.strip()]
f.close()
lines.sort()
for line in lines:
print (line)
Well, just a part:
def prompt_int(prompt=""):
while True:
val = input(prompt)
if val in ("1", "2"):
return int(val), True
Will ask again and again. And return when the user enter "1" or "2"!
But better: "if val in "12":
def prompt_int(prompt=""):
while True:
val = input(prompt)
if val.isdigit():
return int(val)
Hi if you dont want to have valid values send to your you could change your code as the function above.
But you could also change it to do the system exits:
def prompt_int(prompt="", authorized=()):
while True:
val = raw_input(prompt)
if val.isdigit():
if int(val) in authorized:
return int(val)
else:
sys.exit("Bla bla bla too bad")
def prompt_int(prompt=''):
while True:
if status == prompt_int(prompt=''):
This line will look for the name "status" in the global namespace (module's namespace), and raise a NameError if there's no global variable named 'status'.
If there's one, it will then recursively calls prompt_int without any possible termination, resulting theoretically in an endless recursion, but practically (in CPython at least) in a RuntimeError when it will hit the maximum recursion depth.
There are also quite a few other things that won't work as you expect:
val = input(prompt)
if val in (1,2):
In Python 3.x, val will be a string, so it will never compare equal to an int. In Python 2.x, input() is a shortcut for eval(raw_input()), which might return an int, but is also a huge security flaw since it unconditionnally execute untrusted code.
return int(val)
return true
The second return statement will never be executed, obviously, since the function will exit at the first one.
A simpler implementation might look like this:
# rebinds raw_input to input for python < 3
import sys
if sys.version_info.major < 3:
input = raw_input
def prompt_int(prompt='', choices=None):
while True:
val = input(prompt)
try:
val = int(val)
if choices and val not in choices:
raise ValueError("{} is not in {}".format(val, choices))
return val
except (TypeError, ValueError) as e:
print(
"Not a valid number ({}), please try again".format(e)
)
While we're at it, there's room for improvement in other parts of your code. Let's start with this:
def get_bool_input(prompt=''):
while True:
val = input(prompt).lower()
if val == 'yes':
return True
elif val == 'no':
return False
else:
sys.exit("Not a valid input (yes/no is expected) please try again")
First point: your naming is not consistent. If your other function is named prompt_int, this one should be named prompt_bool. Also, you have one function (prompt_int) looping forever and the other one exiting the whole program on invalid input, which is another inconsistency. If you want to allow the user to exit on any prompt, provide an explicit option for it, ie:
def prompt_bool(prompt, quit='Q'):
prompt += " (hit '{}' to exit) : ".format(quit)
while True:
val = input(prompt).strip().upper()
if val == quit:
sys.exit("Goodbye")
elif val == 'yes':
return True
elif val == 'no':
return False
else:
print "Invalid input '{}', please try again".format(val)
Of course you then want to provide the same option in prompt_int(), which leads to a more generic function:
def get_input_or_quit(prompt, quit="Q"):
prompt += " (hit '{}' to exit) : ".format(quit)
val = input(prompt).strip()
if val.upper() == quit:
sys.exit("Goodbye")
return val
def prompt_bool(prompt):
while True:
val = get_input_or_quit(prompt).lower()
if val == 'yes':
return True
elif val == 'no':
return False
else:
print "Invalid input '{}', please try again".format(val)
And of course you also replace the call to input by a call to get_input_or_quit in prompt_int.
We could go on for long - splitting all your code in distinct, self-contained function, writing a "main()" function to drive them (instead of having the "main" part at the top level), and obviously using the operator module instead of eval().

Force case on dictionary to compare user input in Python

I'm making a user input decision tree and I want to force the dictionary that the input is being compared to into lowercase. I've placed .lower() at various points and keep getting errors.
not_found = True
while True:
if OPTIONS == "1" or 'a':
ARTIST_PICK = str(raw_input(
"Please pick an artist\n"
"Or Q to quit: ")).lower
print ARTIST_PICK
**entries = allData(filename).data_to_dict()
for d in entries:
arts = d['artist']**
if ARTIST_PICK in arts:
print "found it"
elif ARTIST_PICK == 'q':
break
else:
print "Sorry, that artist could not be found. Choose again."
not_found = False
This is a sample of the "entries" I'm trying to make lower and compare the user input to:
[{'album': 'Nikki Nack', 'song': 'Find a New Way', 'datetime': '2014-12-03 09:08:00', 'artist': 'tUnE-yArDs'},]
If your problem was just comparing the artist names, then you could use list comprehension to make everything lowercase.
entries = allData(filename).data_to_dict()
if ARTIST_PICK in [ d['artist'].lower() for d in entries ]:
print("found it")
elif ARTIST_PICK == "q":
break
else
print("Sorry, that artist could not be found. Choose again.")
Or if you'd rather use a for loop (rearranged a little for readability):
if(ARTIST_PICK != 'q'):
entries = allData(filename).data_to_dict()
found = False
for d in entries:
if ARTIST_PICK == d['artist'].lower():
found = True
break
elif ARTIST_PICK == "q":
break
if(found):
print("found it")
else:
print("Sorry, that artist could not be found. Choose again.")
else:
# handle the case where the input is 'q' here if you want
By the way, as a matter of principle you should name your boolean variables as though you were using them in a sentence. Instead of setting a variable not_found to False if the variable isn't found, set a variable named found to False or set not_found to True. Makes things easier in the long run.
ARTIST_PICK = str(raw_input(
"Please pick an artist\n"
"Or Q to quit: ")).lower()

Why doesn't my 'oldUser()' run and why does it start again all the time?

I'm very very new to programming and for a school project (50% of my final grade) I had to create a Python program that did roughly this.
I've had some help from my older brother and my teacher but mainly did it myself with some flow charts etc, so please forgive me if I haven't followed conventional rules and things of this nature, or if my code is messy. I will finalise it, just needed bait of help/support from the pro's.
This is my code and I have an issue with it. Once I have pressed 'y' and then 'y' again on the displayMenu() why doesn't it run oldUser()
Also, if any of you have any suggestion on what could make my code better, or I could improve it would be very helpful and I will take it on board.
import os # allows me to use functions defined elsewhere. os module allows for multi platforming.
import sys
words = []
users = {}
status = ""
def teacher_enter_words():
done = False
print 'Hello, please can you enter a word and definition pair.'
while not done:
word = raw_input('\nEnter a word: ')
deff = raw_input('Enter the definition: ')
# append a tuple to the list so it can't be edited.
words.append((word, deff))
add_word = raw_input('Add another word? (y/n): ')
if add_word.lower() == 'n':
done = True
def student_take_test():
student_score = 0
for pair in words:
print 'Definition:', pair[1]
inp = raw_input('Enter word: ')
student_score += check_error(pair[0], inp)
print 'Correct spelling:', pair[0], '\n'
print 'Your score:', student_score
def check_error(correct, inputt):
len_c = len(correct)
len_i = len(inputt)
# threshold is how many incorrect letters do we allow before a
# minor error becomes a major error.
# 1 - allow 1 incorrect letter for a minor error ( >= 2 becomes major error)
threshold = 1
# immediately check if the words are the same length
num_letters_incorrect = abs(len_c - len_i) # abs() method returns value of x - positive dist between x and zero
if num_letters_incorrect == 0:
for i in xrange(0, len(correct)):
if correct[i] != inputt[i]:
num_letters_incorrect += 1
if num_letters_incorrect <= threshold:
if num_letters_incorrect == 0:
return 2 # no incorrect letter.
else:
return 1 # minor error.
else:
return 0 # major error.
def displayMenu():
status = raw_input('Are you a registered user? y/n?: ')
if status == raw_input == 'y':
oldUser()
elif status == 'n':
newUser()
def newUser():
createLogin = raw_input('Create login name: ')
if createLogin in users:
print '\nLogin name already exist!\n'
else:
createPassw = raw_input('Create password: ')
users[createLogin] = createPassw
print '\nUser created!\n'
def oldUser():
login = raw_input('Enter login name: ')
passw = raw_input('Enter password: ')
if login in users and users[login] == passw:
print '\nLogin successful!\n'
else:
print "\nUser doesn't exist or wrong password!\n"
if __name__ == '__main__':
running = True
while running:
os.system('cls' if os.name == 'nt' else 'clear') # multi-platform, executing a shell command
reg = raw_input('Do you want to start the program? y/n?').lower()
if reg == 'y' or reg == 'yes':
displayMenu()
else: sys.exit(0)
inp = raw_input('Are you a Teacher or a Student? (t/s): ').lower()
if inp == 't' or inp == 'teacher':
teacher_enter_words()
else:
student_take_test()
running = False
raw_input is a function. status == raw_input == 'y' will never be true: that is comparing status with the function, and with 'y'.
I suspect that's simply a typo, and you just meant if status == 'y':

Categories

Resources