Skip to content

Commit de7093b

Browse files
committed
now with pictures!
1 parent 4bee483 commit de7093b

File tree

6 files changed

+129
-51
lines changed

6 files changed

+129
-51
lines changed

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
---
2-
title: "Playful Python: Learning the language through games and puzzles"
3-
author: Ken Youens-Clark
4-
...
5-
61
# Introduction
72

83
> "The only way to learn a new programming language is by writing programs in it." - Dennis Ritchie
@@ -43,7 +38,7 @@ $ echo $PATH
4338

4439
Probably each directory is separated by a colon (`:`). *The order of the directories matters!* For instance, it's common to have more than one version of Python installed. When you type `python` on the command line, the directories in your `$PATH` are searched in order, and the first `python` found is the one that is used (and it's probably Python version 2!)
4540

46-
You could execute `new.py` by giving the full path to the program, e.g., `$HOME/work/playful_python/bin/new.py`, but that's really tedious. It's best to put `new.py` into one of the directories that is already in your `$PATH` like maybe `/usr/local/bin`. The problem is that you probably need administrator privileges to write to most of the directories that are in your `$PATH.`. If you are working on your laptop, this is probably not a problem, but if you are on a shared system, you probably won't be able to copy the program into your `$PATH` directories.
41+
You could execute `new.py` by giving the full path to the program, e.g., `$HOME/work/playful_python/bin/new.py`, but that's really tedious. It's best to put `new.py` into one of the directories that is already in your `$PATH` like maybe `/usr/local/bin`. The problem is that you probably need administrator privileges to write to most of the directories that are in your `$PATH`. If you are working on your laptop, this is probably not a problem, but if you are on a shared system, you probably won't be able to copy the program into your `$PATH` directories.
4742

4843
An alternative is to alter your `$PATH` to include the directory where `new.py` is located. E.g., if `new.py` is in `$HOME/work/playful_python/bin/`, then add this directory to your `$PATH` -- probably by editing `.bashrc` or `.bash_profile` located in your `$HOME` directory (if you use `bash`). See the documentation for your shell of choice to understand how to edit and persist your `$PATH`.
4944

bin/compile.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,14 @@ def main():
8585
book_file = os.path.join(out_dir, 'book.md')
8686

8787
with open(book_file, 'wt') as fh:
88-
fh.write('\\setcounter{tocdepth}{2}\\tableofcontents\n\\newpage\n\n')
88+
#fh.write('\\setcounter{tocdepth}{2}\\tableofcontents\n\\newpage\n\n')
89+
90+
title = 'TITLE.md'
91+
if os.path.isfile(title):
92+
fh.write(open(title).read())
93+
fh.write('\n\n\\newpage\n\n')
94+
95+
fh.write('\\setcounter{tocdepth}{2}\\tableofcontents\n\n')
8996

9097
top_readme = 'README.md'
9198
if os.path.isfile(top_readme):

book.md

Lines changed: 106 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
\setcounter{tocdepth}{2}\tableofcontents
2-
\newpage
3-
41
---
52
title: "Playful Python: Learning the language through games and puzzles"
63
author: Ken Youens-Clark
74
...
85

6+
![The Playful Python](./images/playful.png)
7+
8+
9+
\newpage
10+
11+
\setcounter{tocdepth}{2}\tableofcontents
12+
913
# Introduction
1014

1115
> "The only way to learn a new programming language is by writing programs in it." - Dennis Ritchie
@@ -46,7 +50,7 @@ $ echo $PATH
4650

4751
Probably each directory is separated by a colon (`:`). *The order of the directories matters!* For instance, it's common to have more than one version of Python installed. When you type `python` on the command line, the directories in your `$PATH` are searched in order, and the first `python` found is the one that is used (and it's probably Python version 2!)
4852

49-
You could execute `new.py` by giving the full path to the program, e.g., `$HOME/work/playful_python/bin/new.py`, but that's really tedious. It's best to put `new.py` into one of the directories that is already in your `$PATH` like maybe `/usr/local/bin`. The problem is that you probably need administrator privileges to write to most of the directories that are in your `$PATH.`. If you are working on your laptop, this is probably not a problem, but if you are on a shared system, you probably won't be able to copy the program into your `$PATH` directories.
53+
You could execute `new.py` by giving the full path to the program, e.g., `$HOME/work/playful_python/bin/new.py`, but that's really tedious. It's best to put `new.py` into one of the directories that is already in your `$PATH` like maybe `/usr/local/bin`. The problem is that you probably need administrator privileges to write to most of the directories that are in your `$PATH`. If you are working on your laptop, this is probably not a problem, but if you are on a shared system, you probably won't be able to copy the program into your `$PATH` directories.
5054

5155
An alternative is to alter your `$PATH` to include the directory where `new.py` is located. E.g., if `new.py` is in `$HOME/work/playful_python/bin/`, then add this directory to your `$PATH` -- probably by editing `.bashrc` or `.bash_profile` located in your `$HOME` directory (if you use `bash`). See the documentation for your shell of choice to understand how to edit and persist your `$PATH`.
5256

@@ -1126,9 +1130,9 @@ But I find that fairly hard to read.
11261130

11271131
# Chapter 6: Telephone
11281132

1129-
Perhaps you remember the game of "Telephone" where a message is secretly passed through a series of intermediaries and then the result at the end of the chain is compared with how it started? This is like that, only we're going to take some`text` (from the command line or a file) and mutate it by some percentage `-m|--mutations` (a number between 0 and 1, default `0.1` or 10%) and then print out the resulting text.
1133+
Perhaps you remember the game of "Telephone" where a message is secretly passed through a series of intermediaries and then the result at the end of the chain is compared with how it started? This is like that, only we're going to take some `text` (from the command line or a file) and mutate it by some percentage `-m|--mutations` (a number between 0 and 1, default `0.1` or 10%) and then print out the resulting text.
11301134

1131-
Each mutation to the text should be chosen using the `random` module, so your program will also need to accept a `-s|--seed` option to pass to the `random.seed` function for testing purposes. Print the resulting text after making the appropriate number of mutations.
1135+
Each mutation to the text should be chosen using the `random` module, so your program will also need to accept a `-s|--seed` option (default `None`) to pass to the `random.seed` function for testing purposes. Print the resulting text after making the appropriate number of mutations.
11321136

11331137
````
11341138
$ ./telephone.py
@@ -1166,39 +1170,27 @@ $ ./telephone.py -s 1 -m .5 ../inputs/fox.txt
11661170
Thakqkrck&brow- fo[ jumps#oWe,*L/C lxdy dogos
11671171
````
11681172

1169-
## Mutations in DNA
1170-
1171-
For what it's worth, this is how DNA changes over time. The machinery to copy DNA makes mistakes, and mutations randomly occur. Many times the change is in a part of the DNA that doesn't affect the organism or is a "synonymous" change that doesn't end up affecting the function of the DNA. Our example will only change characters to other characters, what are called "point mutations" or "single nucleotide variations" (SNV) or "single nucleotide polymorphisms" (SNP) in biology. We could write a version that would also randomly delete or insert new characters which are called them "in-dels" (insertion-deletions) in biology.
1172-
1173-
Mutations (that don't result in the demise of the organism) occur at a fairly standard rate, so counting the number of point mutations (AKA ) between a conserved region of any two organisms, we can estimate how long ago they diverged from a common ancestor!
1174-
1175-
We can revisit the output of this program later by using the Hamming distance to find how many changes we'd need to make to the output to regain the input.
1176-
1177-
## Hints
1178-
1179-
To create a combined error/usage statement for the `--mutations` error, look at `parser.error` in `argparse`.
1180-
1181-
To select a character position to change, I suggest using `random.choice` and a `range` from length of the incoming text. With that, you'll need to alter the character at that position, but you'll find that strings in Python are *immutable*. For instance, if I wanted to change "candle" into "handle":
1173+
Hints:
11821174

1175+
* To create a combined error/usage statement for the `--mutations` error, look at `parser.error` in `argparse`.
1176+
* To select a character position to change, I suggest using `random.choice` and a `range` from length of the incoming text. With that, you'll need to alter the character at that position, but you'll find that strings in Python are *immutable*. For instance, if I wanted to change "candle" into "handle":
11831177
````
11841178
>>> s = 'candle'
11851179
>>> s[0] = 'h'
11861180
Traceback (most recent call last):
11871181
File "<stdin>", line 1, in <module>
11881182
TypeError: 'str' object does not support item assignment
11891183
````
1190-
1191-
So, I need to create a *new string* that has `h` joined to the rest of the string `s` after the zeroth position. How could you do that?
1192-
1193-
For the replacement value, you should use `random.choice` from the union of the `string` class's `ascii_letters` and `punctuation`:
1194-
1184+
* So, I need to create a *new string* that has `h` joined to the rest of the string `s` after the zeroth position. How could you do that?
1185+
* For the replacement value, you should use `random.choice` from the union of the `string` class's `ascii_letters` and `punctuation`:
11951186
````
11961187
>>> import string
11971188
>>> string.ascii_letters
11981189
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
11991190
>>> string.punctuation
12001191
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
12011192
````
1193+
12021194
\newpage
12031195

12041196
## Solution
@@ -1275,6 +1267,96 @@ For the replacement value, you should use `random.choice` from the union of the
12751267
69 main()
12761268
````
12771269

1270+
\newpage
1271+
1272+
## Discussion
1273+
1274+
The number of mutations will be proportional to the length of the text
1275+
1276+
````
1277+
>>> text = 'The quick brown fox jumps over the lazy dog.'
1278+
>>> len_text = len(text)
1279+
>>> len_text
1280+
44
1281+
````
1282+
1283+
Since we chose the `--mutations` to be a `float` between 0 and 1, we can multiply that by the length to get the number of mutations to introduce. Since that number will likely be another `float` and we can introduce a partial number of mutations, we can use `int` to truncate the number to an integer value.
1284+
1285+
````
1286+
>>> mutations = .1
1287+
>>> int(mutations * len_text)
1288+
4
1289+
````
1290+
1291+
So we can use that number in a `for` loop with `range(4)` to modify four characters. To choose a character in the text to modify, I suggested to use `random.choice`:
1292+
1293+
````
1294+
>>> import random
1295+
>>> random.choice(range(len_text))
1296+
1
1297+
>>> random.choice(range(len_text))
1298+
22
1299+
````
1300+
1301+
If you assign that to a value like `i` (for "integer" and/or "index", it's pretty common to use `i` for this kind of value), then you could get the character at that position:
1302+
1303+
````
1304+
>>> i = random.choice(range(len_text))
1305+
>>> i
1306+
4
1307+
>>> text[i]
1308+
'q'
1309+
````
1310+
1311+
Now we saw earlier that we can't just change the `text`:
1312+
1313+
````
1314+
>>> text[i] = 'x'
1315+
Traceback (most recent call last):
1316+
File "<stdin>", line 1, in <module>
1317+
TypeError: 'str' object does not support item assignment
1318+
````
1319+
1320+
So we're going to have to create a *new* string using the text before and after `i` which we can get with string slices using `text[start:stop]`. If you leave out "start", Python starts at `0` (the beginning of the string), and if you leave out "stop" then it goes to the end, so `text[:]` is a copy of the entire string.
1321+
1322+
The bit before `i` is:
1323+
1324+
````
1325+
>>> text[:i]
1326+
'The '
1327+
````
1328+
1329+
And after `i` (skipping `i` itself, of course):
1330+
1331+
````
1332+
>>> text[i+1:]
1333+
'uick brown fox jumps over the lazy dog.'
1334+
````
1335+
1336+
There are many ways to join strings together into new strings, and the `+` operator is perhaps the simplest. So now we need some new character to insert in the middle which we can get with `random.choice` again, this time choosing from all the letters of the alphabet plus punctuation:
1337+
1338+
````
1339+
>>> import string
1340+
>>> alpha = string.ascii_letters + string.punctuation
1341+
>>> random.choice(alpha)
1342+
'n'
1343+
````
1344+
1345+
So to put it together, we overwrite the existing `text` so as to accumulate the changes over the iterations:
1346+
1347+
````
1348+
>>> text = text[:i] + random.choice(alpha) + text[i+1:]
1349+
>>> text
1350+
'The vuick brown fox jumps over the lazy dog.'
1351+
````
1352+
1353+
## Mutations in DNA
1354+
1355+
For what it's worth, this is (sort of) how DNA changes over time. The machinery to copy DNA makes mistakes, and mutations randomly occur. Many times the change has no deleterious affect on the organism. Our example only changes characters to other characters, what are called "point mutations" or "single nucleotide variations" (SNV) or "single nucleotide polymorphisms" (SNP) in biology, but we could write a version that would also randomly delete or insert new characters which are called them "in-dels" (insertion-deletions) in biology.
1356+
1357+
Mutations (that don't result in the demise of the organism) occur at a fairly standard rate, so counting the number of mutations between a conserved region of any two organisms can allow an estimate of how long ago they diverged from a common ancestor! We can revisit the output of this program later by using the Hamming distance to find how many changes we'd need to make to the output to regain the input.
1358+
1359+
12781360
\newpage
12791361

12801362
# Chapter 7: Bottles of Beer Song

playful_python.pdf

205 KB
Binary file not shown.

telephone/README.md

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Telephone
22

3-
Perhaps you remember the game of "Telephone" where a message is secretly passed through a series of intermediaries and then the result at the end of the chain is compared with how it started? This is like that, only we're going to take some`text` (from the command line or a file) and mutate it by some percentage `-m|--mutations` (a number between 0 and 1, default `0.1` or 10%) and then print out the resulting text.
3+
Perhaps you remember the game of "Telephone" where a message is secretly passed through a series of intermediaries and then the result at the end of the chain is compared with how it started? This is like that, only we're going to take some `text` (from the command line or a file) and mutate it by some percentage `-m|--mutations` (a number between 0 and 1, default `0.1` or 10%) and then print out the resulting text.
44

55
Each mutation to the text should be chosen using the `random` module, so your program will also need to accept a `-s|--seed` option (default `None`) to pass to the `random.seed` function for testing purposes. Print the resulting text after making the appropriate number of mutations.
66

@@ -40,36 +40,23 @@ $ ./telephone.py -s 1 -m .5 ../inputs/fox.txt
4040
Thakqkrck&brow- fo[ jumps#oWe,*L/C lxdy dogos
4141
````
4242

43-
## Mutations in DNA
44-
45-
For what it's worth, this is how DNA changes over time. The machinery to copy DNA makes mistakes, and mutations randomly occur. Many times the change is in a part of the DNA that doesn't affect the organism or is a "synonymous" change that doesn't end up affecting the function of the DNA. Our example will only change characters to other characters, what are called "point mutations" or "single nucleotide variations" (SNV) or "single nucleotide polymorphisms" (SNP) in biology. We could write a version that would also randomly delete or insert new characters which are called them "in-dels" (insertion-deletions) in biology.
46-
47-
Mutations (that don't result in the demise of the organism) occur at a fairly standard rate, so counting the number of mutations between a conserved region of any two organisms can allow an estimate of how long ago they diverged from a common ancestor!
48-
49-
We can revisit the output of this program later by using the Hamming distance to find how many changes we'd need to make to the output to regain the input.
50-
51-
## Hints
52-
53-
To create a combined error/usage statement for the `--mutations` error, look at `parser.error` in `argparse`.
54-
55-
To select a character position to change, I suggest using `random.choice` and a `range` from length of the incoming text. With that, you'll need to alter the character at that position, but you'll find that strings in Python are *immutable*. For instance, if I wanted to change "candle" into "handle":
43+
Hints:
5644

45+
* To create a combined error/usage statement for the `--mutations` error, look at `parser.error` in `argparse`.
46+
* To select a character position to change, I suggest using `random.choice` and a `range` from length of the incoming text. With that, you'll need to alter the character at that position, but you'll find that strings in Python are *immutable*. For instance, if I wanted to change "candle" into "handle":
5747
````
5848
>>> s = 'candle'
5949
>>> s[0] = 'h'
6050
Traceback (most recent call last):
6151
File "<stdin>", line 1, in <module>
6252
TypeError: 'str' object does not support item assignment
6353
````
64-
65-
So, I need to create a *new string* that has `h` joined to the rest of the string `s` after the zeroth position. How could you do that?
66-
67-
For the replacement value, you should use `random.choice` from the union of the `string` class's `ascii_letters` and `punctuation`:
68-
54+
* So, I need to create a *new string* that has `h` joined to the rest of the string `s` after the zeroth position. How could you do that?
55+
* For the replacement value, you should use `random.choice` from the union of the `string` class's `ascii_letters` and `punctuation`:
6956
````
7057
>>> import string
7158
>>> string.ascii_letters
7259
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
7360
>>> string.punctuation
7461
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
75-
````
62+
````

telephone/discussion.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,10 @@ So to put it together, we overwrite the existing `text` so as to accumulate the
7676
>>> text
7777
'The vuick brown fox jumps over the lazy dog.'
7878
````
79+
80+
## Mutations in DNA
81+
82+
For what it's worth, this is (sort of) how DNA changes over time. The machinery to copy DNA makes mistakes, and mutations randomly occur. Many times the change has no deleterious affect on the organism. Our example only changes characters to other characters, what are called "point mutations" or "single nucleotide variations" (SNV) or "single nucleotide polymorphisms" (SNP) in biology, but we could write a version that would also randomly delete or insert new characters which are called them "in-dels" (insertion-deletions) in biology.
83+
84+
Mutations (that don't result in the demise of the organism) occur at a fairly standard rate, so counting the number of mutations between a conserved region of any two organisms can allow an estimate of how long ago they diverged from a common ancestor! We can revisit the output of this program later by using the Hamming distance to find how many changes we'd need to make to the output to regain the input.
85+

0 commit comments

Comments
 (0)