Skip to content

Commit cb5b330

Browse files
committed
how to write bigger things
1 parent b521029 commit cb5b330

File tree

1 file changed

+220
-0
lines changed

1 file changed

+220
-0
lines changed

basics/larger-program.md

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# Writing a larger program
2+
3+
Now we know enough about Python for creating a program that is actually
4+
useful. Awesome!
5+
6+
In this tutorial we'll write a program that reads questions and answers
7+
in a text file and asks them. For example, this file would make the
8+
program ask what "text displaying function" and "text asking function"
9+
are:
10+
11+
```
12+
text displaying function = print
13+
text asking function = input
14+
```
15+
16+
This might seem useless to you right now, but a program like this can
17+
actually be really useful for learning different kinds of things. I
18+
originally wrote a program like this to study words of a foreign
19+
language, but then I realized that I could study pretty much anything
20+
with it.
21+
22+
But there are many things the program needs to do and writing it seems
23+
really difficult and complicated! How the heck can we do this?
24+
25+
## Write functions
26+
27+
Our program will need to do several different things:
28+
29+
1. Read the questions from a file.
30+
2. Ask the questions.
31+
3. Print statistics about how many questions were answered correctly
32+
and how many wrong.
33+
34+
Now everything seems much easier. We know how to do each of these steps
35+
one by one, but doing it all at once would be difficult. In situations
36+
like this **it's important to [define
37+
functions](defining-functions.md)**. We are going to write a
38+
`read_questions` function, an `ask_questions` function and a `stats`
39+
function.
40+
41+
Let's start with the function that reads the question file:
42+
43+
```py
44+
def read_questions(filename):
45+
answers = {}
46+
with open(questionfile, 'r') as f:
47+
for line in f:
48+
line = line.strip()
49+
if line: # ignore empty lines
50+
question, answer = line.split('=')
51+
answers[question.strip()] = answer.strip()
52+
return answers
53+
```
54+
55+
At this point it's best to try out the function to see how it works. You
56+
need to create a `questions.txt` file like the one in the beginning of
57+
this tutorial if you didn't create it already.
58+
59+
**TODO:** Instructions for using the -i switch.
60+
61+
```py
62+
>>> read_questions('questions.txt')
63+
{'text displaying function': 'print', 'text asking function': 'input'}
64+
>>>
65+
```
66+
67+
If your function doesn't work correctly it doesn't matter, and fixing
68+
the problem is easy because the function is so short. This is one of the
69+
reasons why we write functions.
70+
71+
Next we'll write the rest of the functions the same way, first writing
72+
and then testing and fixing. Here are my versions of them:
73+
74+
```py
75+
def ask_questions(answers):
76+
correct = []
77+
wrong = []
78+
for question, answer in answers.items():
79+
if input(question + ' = ').strip() == answer:
80+
print("Correct!")
81+
correct.append(question)
82+
else:
83+
print("Wrong! The correct answer is %s." % answer)
84+
wrong.append(question)
85+
return (correct, wrong)
86+
87+
def stats(correct, wrong, answers):
88+
print("\n**** STATS ****\n")
89+
print("You answered", len(correct), "questions correctly and",
90+
len(wrong), "questions wrong.")
91+
if wrong:
92+
print("These would have been the correct answers:")
93+
for question in wrong:
94+
print(' ', question, '=', answers[question])
95+
```
96+
97+
Let's try them out.
98+
99+
```py
100+
>>> answers = read_questions('questions.txt')
101+
>>> correct, wrong = ask_questions(answers)
102+
text displaying function = print
103+
Correct!
104+
text asking function = elif
105+
Wrong! The correct answer is input.
106+
>>> correct
107+
['text displaying function']
108+
>>> wrong
109+
['text asking function']
110+
>>> stats(correct, wrong, answers)
111+
112+
*** STATS ***
113+
114+
You answered 1 questions right and 1 questions wrong.
115+
These would have been the correct answers:
116+
text asking function = input
117+
>>>
118+
```
119+
120+
Everything is working! Now we just need something that runs everything
121+
because we don't want to type this out on the `>>>` prompt every time.
122+
123+
You might have noticed that the stats function printed `1 questions`
124+
instead of `1 question`, and it looks a bit weird. You can modify the
125+
`print_stats` function to fix this if you want to.
126+
127+
## The main function
128+
129+
The last function in a program like this is usually called `main` and it
130+
runs the program using other functions. Our main function consists of
131+
mostly the same pieces of code that we just tried out on the `>>>`
132+
prompt.
133+
134+
```py
135+
def main():
136+
filename = input("Name of the question file: ")
137+
answers = read_questions(filename)
138+
correct, wrong = ask_questions(answers)
139+
stats(correct, wrong, answers)
140+
```
141+
142+
The last thing we need to add is these two lines:
143+
144+
```py
145+
if __name__ == '__main__':
146+
main()
147+
```
148+
149+
The `__name__` variable is set differently depending on how we run the
150+
file, and **it's `'__main__'` when we run the file directly instead of
151+
importing**. So if we run the file normally it asks us the words, and if
152+
we import it instead we can still run the functions one by one. If you
153+
want to know more about `__name__` just make a file that prints it and
154+
run it in different ways.
155+
156+
Now the whole program looks like this:
157+
158+
```py
159+
def read_questions(filename):
160+
answers = {}
161+
with open(questionfile, 'r') as f:
162+
for line in f:
163+
line = line.strip()
164+
if line: # ignore empty lines
165+
question, answer = line.split('=')
166+
answers[question.strip()] = answer.strip()
167+
return answers
168+
169+
def ask_questions(answers):
170+
correct = []
171+
wrong = []
172+
for question, answer in answers.items():
173+
if input('%s = ' % question).strip() == answer:
174+
print("Correct!")
175+
correct.append(question)
176+
else:
177+
print("Wrong! The correct answer is %s." % answer)
178+
wrong.append(question)
179+
return (correct, wrong)
180+
181+
def stats(correct, wrong, answers):
182+
print("\n**** STATS ****\n")
183+
print("You answered", len(correct), "questions correctly and",
184+
len(wrong), "questions wrong.")
185+
if wrong:
186+
print("These would have been the correct answers:")
187+
for question in wrong:
188+
print(' ', question, '=', answers[question])
189+
190+
def main():
191+
filename = input("Name of the question file: ")
192+
answers = read_questions(filename)
193+
correct, wrong = ask_questions(answers)
194+
stats(correct, wrong, answers)
195+
196+
if __name__ == '__main__':
197+
main()
198+
```
199+
200+
This is just the beginning. Now [you can](../LICENSE) take your word
201+
asking program and make your own version of it that suits **your**
202+
needs. Then you can share it with your friends so they will find it
203+
useful as well.
204+
205+
## Summary
206+
207+
- Make multiple functions when your program needs to do multiple things.
208+
Each function should do one thing.
209+
- Try out the functions on the `>>>` prompt when you want to check if
210+
they work correctly.
211+
- `__name__` is `'__main__'` when the program is supposed to run, and
212+
something else when it's imported.
213+
214+
***
215+
216+
You may use this tutorial freely at your own risk. See
217+
[LICENSE](../LICENSE).
218+
219+
[Previous](defining-functions.md) | [Next](what-is-true.md) |
220+
[List of contents](../README.md#basics)

0 commit comments

Comments
 (0)