Skip to content

Commit 6c4ec93

Browse files
authored
Merge branch 'master' into celery-async-email
2 parents 5928537 + 5a1d5f8 commit 6c4ec93

File tree

3 files changed

+255
-0
lines changed

3 files changed

+255
-0
lines changed

python-quiz-application/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
This repository holds the code for the Real Python [Build a Quiz Application With Python](https://realpython.com/python-quiz-application/) tutorial.
44

5+
The tutorial uses the [walrus operator](https://realpython.com/python-walrus-operator/), which was introduced in [Python 3.8](https://realpython.com/python38-new-features/). The [`source_code_final_37`](source_code_final/) directory shows a quiz application version that doesn't use the walrus operator and runs on [Python 3.7](https://realpython.com/python37-new-features/).
6+
57
## Dependencies
68

79
If you're running on Python 3.10 or earlier, then you need to install [`tomli`](https://pypi.org/project/tomli/) which is used to read TOML data files. You should first create a virtual environment:
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
[python]
2+
label = "Python"
3+
4+
[[python.questions]]
5+
question = "When was the first known use of the word 'quiz'"
6+
answers = ["1781"]
7+
alternatives = ["1771", "1871", "1881"]
8+
9+
[[python.questions]]
10+
question = "Which built-in function can get information from the user"
11+
answers = ["input"]
12+
alternatives = ["get", "print", "write"]
13+
14+
[[python.questions]]
15+
question = "What's the purpose of the built-in zip() function"
16+
answers = ["To iterate over two or more sequences at the same time"]
17+
alternatives = [
18+
"To combine several strings into one",
19+
"To compress several files into one archive",
20+
"To get information from the user",
21+
]
22+
23+
[[python.questions]]
24+
question = "What does dict.get(key) return if key isn't found in dict"
25+
answers = ["None"]
26+
alternatives = ["key", "True", "False"]
27+
28+
[[python.questions]]
29+
question = "How do you iterate over both the indices and elements in an iterable"
30+
answers = ["enumerate(iterable)"]
31+
alternatives = [
32+
"enumerate(iterable, start=1)",
33+
"range(iterable)",
34+
"range(iterable, start=1)",
35+
]
36+
37+
[[python.questions]]
38+
question = "What's the official name of the := operator"
39+
answers = ["Assignment expression"]
40+
alternatives = [
41+
"Named expression",
42+
"Walrus operator",
43+
"Colon equals operator",
44+
]
45+
46+
[[python.questions]]
47+
question = "What's one effect of calling random.seed(42)"
48+
answers = ["The random numbers are reproducible."]
49+
alternatives = [
50+
"The random numbers are more random.",
51+
"The computer clock is reset.",
52+
"The first random number is always 42.",
53+
]
54+
55+
[[python.questions]]
56+
question = "Which version of Python is the first with TOML support built in"
57+
answers = ["3.11"]
58+
alternatives = ["3.9", "3.10", "3.12"]
59+
60+
[[python.questions]]
61+
question = "How can you run a Python script named quiz.py"
62+
answers = ["python quiz.py", "python -m quiz"]
63+
alternatives = ["python quiz", "python -m quiz.py"]
64+
hint = "One option uses the filename, and the other uses the module name."
65+
66+
[[python.questions]]
67+
question = "What's the name of the list-like data structure in TOML"
68+
answers = ["Array"]
69+
alternatives = ["List", "Sequence", "Set"]
70+
71+
[[python.questions]]
72+
question = "What's a PEP"
73+
answers = ["A Python Enhancement Proposal"]
74+
alternatives = [
75+
"A Pretty Exciting Policy",
76+
"A Preciously Evolved Python",
77+
"A Potentially Epic Prize",
78+
]
79+
hint = "PEPs are used to evolve Python."
80+
explanation = """
81+
Python Enhancement Proposals (PEPs) are design documents that provide
82+
information to the Python community. PEPs are used to propose new features
83+
for the Python language, to collect community input on an issue, and to
84+
document design decisions made about the language.
85+
"""
86+
87+
[[python.questions]]
88+
question = "How can you add a docstring to a function"
89+
answers = [
90+
"By writing a string literal as the first statement in the function",
91+
"By assigning a string to the function's .__doc__ attribute",
92+
]
93+
alternatives = [
94+
"By using the built-in @docstring decorator",
95+
"By returning a string from the function",
96+
]
97+
hint = "They are parsed from your code and stored on the function object."
98+
explanation = """
99+
Docstrings document functions and other Python objects. A docstring is a
100+
string literal that occurs as the first statement in a module, function,
101+
class, or method definition. Such a docstring becomes the .__doc__ special
102+
attribute of that object. See PEP 257 for more information.
103+
104+
There's no built-in @docstring decorator. Many functions naturally return
105+
strings. Such a feature can therefore not be used for docstrings.
106+
"""
107+
108+
[[python.questions]]
109+
question = "When was the first public version of Python released"
110+
answers = ["February 1991"]
111+
alternatives = ["January 1994", "October 2000", "December 2008"]
112+
hint = "The first public version was labeled version 0.9.0."
113+
explanation = """
114+
Guido van Rossum started work on Python in December 1989. He posted
115+
Python v0.9.0 to the alt.sources newsgroup in February 1991. Python
116+
reached version 1.0.0 in January 1994. The next major versions,
117+
Python 2.0 and Python 3.0, were released in October 2000 and December
118+
2008, respectively.
119+
"""
120+
121+
[capitals]
122+
label = "Capitals"
123+
124+
[[capitals.questions]]
125+
question = "What's the capital of Norway"
126+
answers = ["Oslo"]
127+
hint = "Lars Onsager, Jens Stoltenberg, Trygve Lie, and Børge Ousland."
128+
alternatives = ["Stockholm", "Copenhagen", "Helsinki", "Reykjavik"]
129+
explanation = """
130+
Oslo was founded as a city in the 11th century and established as a
131+
trading place. It became the capital of Norway in 1299. The city was
132+
destroyed by a fire in 1624 and rebuilt as Christiania, named in honor
133+
of the reigning king. The city was renamed back to Oslo in 1925.
134+
"""
135+
136+
[[capitals.questions]]
137+
question = "What's the state capital of Texas, USA"
138+
answers = ["Austin"]
139+
alternatives = ["Harrisburg", "Houston", "Galveston", "Columbia"]
140+
hint = "SciPy is held there each year."
141+
explanation = """
142+
Austin is named in honor of Stephen F. Austin. It was purpose-built to
143+
be the capital of Texas and was incorporated in December 1839. Houston,
144+
Harrisburg, Columbia, and Galveston are all earlier capitals of Texas.
145+
"""
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# quiz.py
2+
3+
import pathlib
4+
import random
5+
from string import ascii_lowercase
6+
7+
try:
8+
import tomllib
9+
except ModuleNotFoundError:
10+
import tomli as tomllib
11+
12+
NUM_QUESTIONS_PER_QUIZ = 5
13+
QUESTIONS_PATH = pathlib.Path(__file__).parent / "questions.toml"
14+
15+
16+
def run_quiz():
17+
questions = prepare_questions(
18+
QUESTIONS_PATH, num_questions=NUM_QUESTIONS_PER_QUIZ
19+
)
20+
21+
num_correct = 0
22+
for num, question in enumerate(questions, start=1):
23+
print(f"\nQuestion {num}:")
24+
num_correct += ask_question(question)
25+
26+
print(f"\nYou got {num_correct} correct out of {num} questions")
27+
28+
29+
def prepare_questions(path, num_questions):
30+
topic_info = tomllib.loads(path.read_text())
31+
topics = {
32+
topic["label"]: topic["questions"] for topic in topic_info.values()
33+
}
34+
topic_label = get_answers(
35+
question="Which topic do you want to be quizzed about",
36+
alternatives=sorted(topics),
37+
)[0]
38+
39+
questions = topics[topic_label]
40+
num_questions = min(num_questions, len(questions))
41+
return random.sample(questions, k=num_questions)
42+
43+
44+
def ask_question(question):
45+
correct_answers = question["answers"]
46+
alternatives = question["answers"] + question["alternatives"]
47+
ordered_alternatives = random.sample(alternatives, k=len(alternatives))
48+
49+
answers = get_answers(
50+
question=question["question"],
51+
alternatives=ordered_alternatives,
52+
num_choices=len(correct_answers),
53+
hint=question.get("hint"),
54+
)
55+
if set(answers) == set(correct_answers):
56+
print("⭐ Correct! ⭐")
57+
correct = True
58+
else:
59+
is_or_are = " is" if len(correct_answers) == 1 else "s are"
60+
print("\n- ".join([f"No, the answer{is_or_are}:"] + correct_answers))
61+
correct = False
62+
63+
if "explanation" in question:
64+
print(f"\nEXPLANATION:\n{question['explanation']}")
65+
66+
return 1 if correct else 0
67+
68+
69+
def get_answers(question, alternatives, num_choices=1, hint=None):
70+
print(f"{question}?")
71+
labeled_alternatives = dict(zip(ascii_lowercase, alternatives))
72+
if hint:
73+
labeled_alternatives["?"] = "Hint"
74+
75+
for label, alternative in labeled_alternatives.items():
76+
print(f" {label}) {alternative}")
77+
78+
while True:
79+
plural_s = "" if num_choices == 1 else f"s (choose {num_choices})"
80+
answer = input(f"\nChoice{plural_s}? ")
81+
answers = set(answer.replace(",", " ").split())
82+
83+
# Handle hints
84+
if hint and "?" in answers:
85+
print(f"\nHINT: {hint}")
86+
continue
87+
88+
# Handle invalid answers
89+
if len(answers) != num_choices:
90+
plural_s = "" if num_choices == 1 else "s, separated by comma"
91+
print(f"Please answer {num_choices} alternative{plural_s}")
92+
continue
93+
94+
invalid = [
95+
answer for answer in answers if answer not in labeled_alternatives
96+
]
97+
if invalid:
98+
print(
99+
f"{invalid[0]} is not a valid choice. "
100+
f"Please use {', '.join(labeled_alternatives)}"
101+
)
102+
continue
103+
104+
return [labeled_alternatives[answer] for answer in answers]
105+
106+
107+
if __name__ == "__main__":
108+
run_quiz()

0 commit comments

Comments
 (0)