Just wondering if there's anything I can improve on, I just started with Python recently and this is one of my first programs.
# Defining Score variables
x = 0
score = x
# Question One
print("What is 1 + 1")
answer_1 = input("a)1\nb)2\nc)3\nd)4\n:")
if answer_1.lower() == "b" or answer_1.lower() == "2":
print("Correct")
x = x + 1
else:
print("Incorrect, 1 + 1 is 2")
# Question Two
print("Who is the 45th president of the United States?")
answer_2 = input("a)Barack Obama\nb)Hillary Clinton\nc)Donald Trump\nd)Tom Brady\n:")
if answer_2.lower() == "c" or answer_2.lower() == "donald trump":
print("Correct")
x = x + 1
else:
print("Incorrect, The 45th president is Donald Trump")
# Question Three
print("True or False... The Toronto Maple Leafs have won 13 Stanley Cups?")
answer_3 = input(":")
if answer_3.lower() == "true" or answer_3.lower() == "t":
print("Correct")
x = x + 1
else:
print("Incorrect")
# Question Four
print("What was the last year the Toronto Maple Leafs won the Stanley Cup?")
answer_4 = input("a)1967\nb)1955\nc)1987\nd)1994\n:")
if answer_4.lower() == "a" or answer_4 == "1967":
print("Correct")
x = x + 1
else:
print("Incorrect, The last time the Toronto Maple Leafs won the Stanley Cup was 1967")
# Question Five
print("True or False... The current Prime Minister of Canada is Pierre Elliot Tredeau?")
answer_5 = input(":")
if answer_5.lower() == "false" or answer_5.lower() == "f":
print("Correct")
x = x + 1
else:
print("Incorrect, The current Prime Minster of Canada is Justin Tredeau")
#Total Score
score = float(x / 5) * 100
print(x,"out of 5, that is",score, "%")
1 Answer 1
1. DRY (Don't Repeat Yourself)
You should define containers for your questions and answers. This way you can avoid having to repeat the whole logic for every question.
As a first step, I would advise to put all your question texts, answer texts, choice texts and correct answer texts in lists.
Then you can write a function that iterates over these lists (at the same time) and does the actual game. For this you can use zip
.
questions = ["What is 1 + 1",
"Who is the 45th president of the United States?",
"True or False... The Toronto Maple Leafs have won 13 Stanley Cups?",
"What was the last year the Toronto Maple Leafs won the Stanley Cup?",
"True or False... The current Prime Minister of Canada is Pierre Elliot Tredeau?"]
answer_choices = ["a)1\nb)2\nc)3\nd)4\n:",
"a)Barack Obama\nb)Hillary Clinton\nc)Donald Trump\nd)Tom Brady\n:",
":",
"a)1967\nb)1955\nc)1987\nd)1994\n:",
":"]
correct_choices = [{"b", "2"},
{"c", "donald trump"},
{"true", "t"},
{"a", "1967"},
{"false", "f"}]
answers = ["1 + 1 is 2",
"The 45th president is Donald Trump",
"",
"The last time the Toronto Maple Leafs won the Stanley Cup was 1967",
"The current Prime Minster of Canada is Justin Tredeau"]
def quiz():
score = 0
for question, choices, correct_choice, answer in zip(questions, answer_choices, correct_choices, answers):
print(question)
user_answer = input(choices).lower()
if user_answer in correct_choice:
print("Correct")
score += 1
else:
print("Incorrect", answer)
print(score, "out of", len(questions), "that is", float(score / len(questions)) * 100, "%")
if __name__ == "__main__":
quiz()
2. OOP (Object-Oriented Programming)
A second step could be trying to encapsulate this whole object of a question into an actual Python object. That is, a question is an object that has a question(text), an answer, answer choices and correct answer choices.
You could define a class
like this:
class Question:
def __init__(self, question, answer, choices, correct):
self.question = question
self.answer = answer
self.choices = choices
self.correct = correct
But this class would be nothing but a data storage for these strings. Whenever you have this, you can use a collections.namedtuple
.
>>> from collections import namedtuple
>>> Question = namedtuple("Question", "question answer choices correct")
>>> q = Question("What is 1 + 1", "1 + 1 is 2", "a)1\nb)2\nc)3\nd)4\n:", {"b", "2"})
>>> q.question
'What is 1 + 1'
>>> q[3]
{'b', '2'}
So a Question
is just a tuple with nice names, so that afterwards q.question = q[0] = 'What is 1 + 1'
, and so on.
from collections import namedtuple
Question = namedtuple("Question", "question answer choices correct")
questions = [Question("What is 1 + 1", "1 + 1 is 2", "a)1\nb)2\nc)3\nd)4\n:", {"b", "2"}),
...]
def quiz():
score = 0
for question in questions:
print(question.question)
user_answer = input(question.choices).lower()
if user_answer in question.correct:
print("Correct")
score += 1
else:
print("Incorrect", question.answer)
print(score, "out of", len(questions), "that is",
float(score / len(questions)) * 100, "%")
if __name__ == "__main__":
quiz()
3. The last push
Last, but not least, you could make enumerating the choices automatic, using string.ascii_lowercase
(which is simply the lowercase alphabet) and str.join
ing it with newlines and the ")"
used to separate a letter from the choice associated with that letter.
You could also pass a list of Question
s directly to the function, allowing making different quizzes (by choosing randomly 5 questions out of a pool of 100 question, for example).
Finally, you could use str.format
to make the print of the final score a bit nicer.
import string
from collections import namedtuple
Question = namedtuple("Question", "question answer choices correct")
questions = [Question("What is 1 + 1", "1 + 1 is 2",
["1", "2", "3", "4"], {"b", "2"}), ...]
def quiz(questions):
score = 0
for question in questions:
print(question.question)
for line in zip(string.ascii_lowercase, question.choices):
print(") ".join(line))
user_answer = input().lower()
if user_answer in question.correct:
print("Correct")
score += 1
else:
print("Incorrect", question.answer)
print("{} out of {} that is {} %".format(score, len(questions), score / len(questions) * 100))
if __name__ == "__main__":
# Use all questions but the first
quiz(questions[1:])
-
2\$\begingroup\$ I don't agree with
str.join
, and would useformat
/f-strings more. But nice answer none the less, :) \$\endgroup\$2017年01月24日 16:31:43 +00:00Commented Jan 24, 2017 at 16:31 -
\$\begingroup\$ haha! Thanks for the help.. Most of what your saying actually does make a little sense as I'm starting to dive into dictionaries and lists now but it's also a bit out of my ball park. Definitely something that I can look back on and understand a little bit down the road. I'll have to start reading up on OOP! \$\endgroup\$CantGoBackToTO– CantGoBackToTO2017年01月24日 16:34:24 +00:00Commented Jan 24, 2017 at 16:34
-
\$\begingroup\$ @StephenPrete To get more comfortable with this, try
l1 = [1,2,3,4]; l2 = ["a", "b", "c", "d"]; list(zip(l1, l2))
in an interactive interpreter to see what it does. Then try it withl2 = ["a", "b", "c"]
to learn aboutzip
stopping at the end of the smaller iterable. \$\endgroup\$Graipher– Graipher2017年01月24日 16:42:24 +00:00Commented Jan 24, 2017 at 16:42
WROOOOONG!
" \$\endgroup\$