Skip to content

Commit d7eb0da

Browse files
committed
Merge branch 'master' into django-flashcards-app
2 parents 8ef6be2 + a3d5117 commit d7eb0da

File tree

16 files changed

+880
-9
lines changed

16 files changed

+880
-9
lines changed

hashtable/02_linear_probing/hashtable.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ def __delitem__(self, key):
4343

4444
def __setitem__(self, key, value):
4545
for index, pair in self._probe(key):
46-
if pair in (None, DELETED) or pair.key == key:
46+
if pair is DELETED:
47+
continue
48+
if pair is None or pair.key == key:
4749
self._slots[index] = Pair(key, value)
4850
break
4951
else:

hashtable/02_linear_probing/test_hashtable.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ def test_should_wrap_index_around_when_collides(mock_hash):
333333
assert hash_table._slots[0] == (False, True)
334334

335335

336-
def test_should_overwrite_deleted(hash_table):
336+
def test_should_not_overwrite_deleted(hash_table):
337337
del hash_table["hola"]
338338
deleted_slot = hash_table._slots.index(DELETED)
339339

@@ -344,7 +344,7 @@ def test_should_overwrite_deleted(hash_table):
344344
hash_table["gracias"] = "thank you"
345345

346346
assert len(hash_table) == 3
347-
assert DELETED not in hash_table._slots
347+
assert DELETED in hash_table._slots
348348

349349

350350
def test_should_raise_memory_error_when_not_enough_capacity():

hashtable/03_autoresize/hashtable.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ def __delitem__(self, key):
4343

4444
def __setitem__(self, key, value):
4545
for index, pair in self._probe(key):
46-
if pair in (None, DELETED) or pair.key == key:
46+
if pair is DELETED:
47+
continue
48+
if pair is None or pair.key == key:
4749
self._slots[index] = Pair(key, value)
4850
break
4951
else:

hashtable/03_autoresize/test_hashtable.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def test_should_wrap_index_around_when_collides(mock_hash):
337337
assert hash_table._slots[0] == (False, True)
338338

339339

340-
def test_should_overwrite_deleted(hash_table):
340+
def test_should_not_overwrite_deleted(hash_table):
341341
del hash_table["hola"]
342342
deleted_slot = hash_table._slots.index(DELETED)
343343

@@ -348,7 +348,7 @@ def test_should_overwrite_deleted(hash_table):
348348
hash_table["gracias"] = "thank you"
349349

350350
assert len(hash_table) == 3
351-
assert DELETED not in hash_table._slots
351+
assert DELETED in hash_table._slots
352352

353353

354354
def test_should_double_capacity():

hashtable/04_load_factor/hashtable.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ def __setitem__(self, key, value):
4949
self._resize_and_rehash()
5050

5151
for index, pair in self._probe(key):
52-
if pair in (None, DELETED) or pair.key == key:
52+
if pair is DELETED:
53+
continue
54+
if pair is None or pair.key == key:
5355
self._slots[index] = Pair(key, value)
5456
break
5557

hashtable/04_load_factor/test_hashtable.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ def test_should_wrap_index_around_when_collides(mock_hash):
360360
assert hash_table._slots[0] == (False, True)
361361

362362

363-
def test_should_overwrite_deleted(hash_table):
363+
def test_should_not_overwrite_deleted(hash_table):
364364
del hash_table["hola"]
365365
deleted_slot = hash_table._slots.index(DELETED)
366366

@@ -371,7 +371,7 @@ def test_should_overwrite_deleted(hash_table):
371371
hash_table["gracias"] = "thank you"
372372

373373
assert len(hash_table) == 3
374-
assert DELETED not in hash_table._slots
374+
assert DELETED in hash_table._slots
375375

376376

377377
def test_should_double_capacity():

python-quiz-application/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Build a Quiz Application With Python
2+
3+
This repository holds the code for the Real Python [Build a Quiz Application With Python](https://realpython.com/python-quiz-application/) tutorial.
4+
5+
## Dependencies
6+
7+
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:
8+
9+
```console
10+
$ python -m venv venv
11+
$ source venv/bin/activate
12+
```
13+
14+
You can then install `tomli` with `pip`:
15+
16+
```console
17+
(venv) $ python -m pip install tomli
18+
```
19+
20+
If you're running Python 3.11 or later, then `tomllib` provides TOML support in the standard library. In this case, you don't need to create a virtual environment or install any third-party dependencies.
21+
22+
## Run the Quiz
23+
24+
Enter one of the `source_code_...` folders. You can then run the quiz by running `quiz.py` as a script:
25+
26+
```console
27+
(venv) $ python quiz.py
28+
```
29+
30+
Check out the `questions.toml` file for a list of the questions that are available (in step 4 and later). Edit this file to add your own questions.
31+
32+
## Author
33+
34+
- **Geir Arne Hjelle**, E-mail: [[email protected]]([email protected])
35+
36+
## License
37+
38+
Distributed under the MIT license. See [`LICENSE`](../LICENSE) for more information.
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: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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 correct := (set(answers) == set(correct_answers)):
56+
print("⭐ Correct! ⭐")
57+
else:
58+
is_or_are = " is" if len(correct_answers) == 1 else "s are"
59+
print("\n- ".join([f"No, the answer{is_or_are}:"] + correct_answers))
60+
61+
if "explanation" in question:
62+
print(f"\nEXPLANATION:\n{question['explanation']}")
63+
64+
return 1 if correct else 0
65+
66+
67+
def get_answers(question, alternatives, num_choices=1, hint=None):
68+
print(f"{question}?")
69+
labeled_alternatives = dict(zip(ascii_lowercase, alternatives))
70+
if hint:
71+
labeled_alternatives["?"] = "Hint"
72+
73+
for label, alternative in labeled_alternatives.items():
74+
print(f" {label}) {alternative}")
75+
76+
while True:
77+
plural_s = "" if num_choices == 1 else f"s (choose {num_choices})"
78+
answer = input(f"\nChoice{plural_s}? ")
79+
answers = set(answer.replace(",", " ").split())
80+
81+
# Handle hints
82+
if hint and "?" in answers:
83+
print(f"\nHINT: {hint}")
84+
continue
85+
86+
# Handle invalid answers
87+
if len(answers) != num_choices:
88+
plural_s = "" if num_choices == 1 else "s, separated by comma"
89+
print(f"Please answer {num_choices} alternative{plural_s}")
90+
continue
91+
92+
if any(
93+
(invalid := answer) not in labeled_alternatives
94+
for answer in answers
95+
):
96+
print(
97+
f"{invalid!r} is not a valid choice. " # noqa
98+
f"Please use {', '.join(labeled_alternatives)}"
99+
)
100+
continue
101+
102+
return [labeled_alternatives[answer] for answer in answers]
103+
104+
105+
if __name__ == "__main__":
106+
run_quiz()

0 commit comments

Comments
 (0)