Merge branch 'master' of ssh://86thumbs.net:3022/waldek/python_course_doc

This commit is contained in:
waldek 2021-11-09 18:46:59 +01:00
commit 5b32090a1d
5 changed files with 3017 additions and 17 deletions

1896
assets/adjectives.txt Normal file

File diff suppressed because it is too large Load Diff

56
assets/login_generator.py Normal file
View File

@ -0,0 +1,56 @@
import random
import string
def load_file(filename):
"""
We load a file and make a list out of it. Note that the same function is
used for both files (both adjectives and subjects). Functions should be
made as generic as possible.
There IS a problem you can fix, some logins will have spaces in them. Try
to remove them in this function!
"""
words = []
with open(filename, "r") as fp:
lines = fp.readlines()
for line in lines:
words.append(line.strip()) # what does strip() do, what does append() do? remember CTRL+Q!
return words
def generate_username():
"""
We'll generate a random pair of adjectives and subjects from two wordlists.
You NEED to have both files in you python project for this to work! Note
the capitalize method call to make it all prettier...
"""
adjectives = load_file("./adjectives.txt")
subjects = load_file("./subjects.txt")
adjective = random.choice(adjectives)
subject = random.choice(subjects)
username = adjective.capitalize() + subject.capitalize()
return username
def generate_password(length=10, complictated=True):
"""
We generate a password with default settings. You can overide these by
changing the arguments in the function call.
"""
password = ""
if complictated:
chars = string.ascii_letters + string.digits + string.punctuation
else:
chars = string.ascii_letters
for i in range(0, length):
password += random.choice(chars)
return password
if __name__ == "__main__":
# let's do some testing!
username_test = generate_username()
print(username_test)
password_test = generate_password()
print(password_test)

61
assets/pwd_cli.py Normal file
View File

@ -0,0 +1,61 @@
import login_generator
def prompt():
"""
We prompt but you KNOW how this works!
"""
response = ""
while not response.isdigit():
response = input("how many login pairs would you like to create?")
return int(response)
def how_long():
"""
And again... (we could combine both prompts, but how?)
"""
response = ""
while not response.isdigit():
response = input("how long should the password be?")
return int(response)
def complex_or_not():
response = ""
while response.lower() not in ["y", "n"]:
response = input("you want complex passwords? (y/n)")
if response.lower() == "y":
return True
else:
return False
def create_login(length, complicated):
"""
We use our library to generate the username and password. The double return
might look confusing but just look at the for loop in the generate_logins
functions and you'll see how it unpacks...
"""
username = login_generator.generate_username()
password = login_generator.generate_password(length, complicated)
return username, password
def generate_logins(number, length, complicated):
"""
Easy no? But what does the i do? Do we really need it's value?
"""
for i in range(0, number):
username, password = create_login(length, complicated)
print("username {}: {}".format(i, username))
print("password {}: {}".format(i, password))
if __name__ == "__main__":
# Here we go!
number_of_logins = prompt()
complicted = complex_or_not()
length = how_long()
generate_logins(number_of_logins, length, complicted)

239
assets/subjects.txt Normal file
View File

@ -0,0 +1,239 @@
Aardvark
African elephant
Albatross
Alley cat
Alligator
Amphibian
Ant
Anteater
Antelope
Ape
Armadillo
Asian elephant
Baboon
Badger
Bat
Bear
Beaver
Beetle
Billy goat
Bison
Boar
Bobcat
Bovine
Bronco
Buck
Buffalo
Bug
Bull
Bunny
BunnyCalf
Camel
Canary
Canine
Caribou
Cat
Caterpillar
Centipede
Chanticleer
Cheetah
Chick
Chimpanzee
Chinchilla
Chipmunk
Clam
Cockatiel
Colt
Condor
Cougar
Cow
Coyote
Crab
Crane
Creature
Crocodile
Crow
Cub
Cur
Cygnet
Deer
Dingo
Dodo
Doe
Dog
Dolphin
Donkey
Dove
Drake
Duck
Eagle
Egret
Elephant
Elk
Emu
Ewe
Falcon
Fawn
Feline
Ferret
Flamingo
Flee
Flies
Foal
Fowl
Fox
Frog
Gander
Gazelle
Gelding
Gerbil
Gibbon
Giraffe
Goat
Goose
Gopher
Gorilla
Grizzly bear
Guinea pig
Hamster
Hare
Hawk
Hedgehog
Heifer
Hippopotamus
Horse
Hound
Hummingbird
Hyena
Ibis
Iguana
Jackal
Jackrabbit
Jaguar
Javalina
Jellyfish
Jenny
Joey
Kangaroo
Kid
Kitten
Kiwi
Koala
Komodo dragon
Krill
Lamb
Lemming
Lemur
Leopard
Lion
Lioness
Llama
Lobster
Lynx
Macaw
Manatee
Marmoset
Marmot
Mink
Minnow
Mite
Mockingbird
Mole
Mongoose
Mongrel
Monkey
Moose
Mouse
Mule
Mustang
Mutt
Nag
Narwhale
Newt
Ocelot
Octopus
Opossum
Orangutan
Orca
Osprey
Ostrich
Otter
Owl
Ox
Pachyderm
Panda
Panther
Parakeet
Parrot
Peacock
Pelican
Penguin
Pheasant
Pig
Pigeon
Piglet
Platypus
Pony
Pooch
Porcupine
Porpoise
Primate
Puppy
Pussycat
Rabbit
Raccoon
Ram
Rat
Reptiles
Rhinoceros
Robin
Rooster
Salamander
Sea lion
Seagull
Seal
Sheep
Sidewinder
Skunk
Sloth
Snail
Snake
Songbird
Sow
Spider
Squid
Squirrel
Stallion
Steer
Stork
Stork
Swan
Tadpole
Tapir
Terrapin
Thoroughbred
Tiger
Toad
Tortoise
Toucan
Turkey
Uakari
Unicorn
Vixen
Vole
Vulture
Wallaby
Walrus
Warthog
Wasp
Weasel
Whale
Wildebeast
Wolf
Wombat
Woodpecker
Worm
X-ray fish
Yak
Zebra

View File

@ -1072,67 +1072,815 @@ There are a couple of things you should definitely read up on.
# While loop
TODO guess the number exercise
We started our python journey with fully linear code.
Next we saw functions which are first **defined** and called afterwards.
Now we'll have a look at **loops**.
In python there are **two** types of loops, a **while** and a **for** loop.
We'll start with the while loop which I see as a loop in *time*.
The for loop is a loop in *space* but we'll get to that one later.
The concept of a while loop is pretty simple.
Code **within** the loop will be executed as long as a **condition** is met.
Consider the code below.
```python3
import time
counter = 0
print("before the loop, counter: {}".format(counter))
while counter <= 10:
print("inside the loop, counter: {}".format(counter))
counter += 1
time.sleep(1)
print("after the loop, counter: {}".format(counter))
```
Two *extra* things might look new to you here.
First the `import time` and `time.sleep(1)`, can you tell me what it does?
Next the `counter += 1` which is called [incrementing](https://stackoverflow.com/questions/1485841/behaviour-of-increment-and-decrement-operators-in-python).
You'll find this feature in most languages.
You can think of it's syntax as *counter equals itself plus 1*.
The *1* can be any number you want though!
When learning the `while` [keyword](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement) there is a *second* keyword you should learn.
It comes in very handy when constructing [infinite loops](https://en.wikipedia.org/wiki/Infinite_loop).
Consider the following code.
```python3
import time
counter = 0
print("before the loop, counter: {}".format(counter))
while True:
print("inside the loop, counter: {}".format(counter))
counter += 1
time.sleep(1)
print("after the loop, counter: {}".format(counter))
```
The `while True` condition is *always* `True` so the loop will **never** exit!
This is what we call an infinite loop.
The `break` keyword was added to the language so we can *break out* of a loop.
The logic is as follows.
```python3
import time
counter = 0
print("before the loop, counter: {}".format(counter))
while True:
print("inside the loop, counter: {}".format(counter))
counter += 1
if counter >= 10:
print("I'll break now!")
break
time.sleep(1)
print("after the loop, counter: {}".format(counter))
```
Infinite loops are a cornerstone of modern programming.
While they might look scary, don't overthink it, you'll get used to them very quickly.
⛑ **When testing out an infinite loop it's sometimes handy to insert a `time.sleep` in it to slow down the execution a bit so you can wrap your head around what's happening.**
🏃 Try it
---
Go back to the Celsius to Farenheit converter and add a while loop to ensure the user put's in only numbers.
# Coding challenge - Guess the number
Now that you know how to repeat code execution we can create our first game!
Everybody knows the *guess the number* game.
The computer chooses a *random* number and the user has to *guess* which number it is.
At each try the computer will till you if the user's number is bigger or smaller *than* the one the computer has in mind.
The flow of the game could be as follows.
```
I have a number in mind...
What's your guess? 50
my number is bigger
What's your guess? 80
my number is smaller
What's your guess? blabla
that's not a number! try again...
What's your guess? 76
yes, that's right! you win!
bye bye...
```
<details>
<summary>Spoiler warning</summary>
```python3
import random
def ask_for_number():
result = input("What's your guess? ")
if result.isdigit():
number = int(result)
return number
else:
return None
if __name__ == "__main__":
number_to_guess = random.randint(0, 100)
print("I have a number in mind...")
while True:
user_number = ask_for_number()
if user_number is None:
print("that's not a number! try again...")
continue
elif number_to_guess == user_number:
print("yes, that's right! you win!")
break
elif number_to_guess > user_number:
print("my number is bigger")
elif number_to_guess < user_number:
print("my number is smaller")
print("bye bye...")
```
</details>
🏃 Try it
---
My *solution* is very basic.
Think of some ways to improve on it.
Can you limit the number of tries?
Can you add a feature to let the user play a *second* game after he/she wins or loses?
Coming up with challenges is on of the most *challenging* aspect op learning how to program.
Your thought process will send you of into unknown territory and will force you to expand you knowledge.
We'll get back to this thought process later, but if you feel like an extra challenge go for it!
# Lists
The different built-in objects we've seen until now, such as `str` and `int` are simple [text](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str) and [numeric](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex) types.
There are other classes of objects that server different purposes.
One of these *groups* is called [sequence types](https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range).
A list in python is pretty much exactly what you think it is.
It is an *object* that groups together *other* objects.
Sounds complicated?
Have a look at the following.
```python3
my_numbers = [1, 2, 44, 60, 70]
print(my_numbers)
```
Easy right?
Compared to [other languages](https://www.cplusplus.com/reference/list/list/) lists in python are *very* flexible.
They can contain objects of different types, and their length can be changed at any time.
Programmers coming from other languages often find this flexibility of python a bug but you should see it as a feature.
```python3
favorite_number = 7
name = "wouter"
date = [1986, 10, 7]
values = [1, date, favorite_number, "hello world", name]
print(values)
```
The code above is just an illustration of the flexibility of lists.
## Creating lists
## Picking elements
Creating lists can be done in two ways, either by using the **square brackets** `[]` or by calling `list`.
When calling `list` it takes **one argument**, which python will iterate over.
For example:
## Slicing lists
```python3
>>> first_list = ["hello", "world", "!"]
>>> first_list
['hello', 'world', '!']
>>> second_list = list("hello world !")
>>> second_list
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', ' ', '!']
>>>
```
## List methods
As a `list` is a different type of object, it has different methods you can invoke on it.
When using tab complete in the python shell we get the following.
```python3
>>> second_list.
second_list.append( second_list.count( second_list.insert( second_list.reverse(
second_list.clear( second_list.extend( second_list.pop( second_list.sort(
second_list.copy( second_list.index( second_list.remove(
>>> second_list.
```
One of the most used methods is `append`.
It is used to add an element to the end of the list.
The second most used method is the `pop` one.
Read the shell code below and you'll understand immediately what they do.
```python3
>>> first_list
['hello', 'world', '!']
>>> first_list.append("coucou")
>>> first_list
['hello', 'world', '!', 'coucou']
>>> first_list.pop()
'coucou'
>>> first_list
['hello', 'world', '!']
>>>
```
🏃 Try it
---
Look at all the methods you can invoke on a list and try them out.
Remember to read the documentation!
## Picking elements and slicing lists
We can pick elements from the list of slice the list as we please.
A code block speaks more than words.
```python3
>>> long_list
['I', 'am', 'a', 'very', 'long', 'list', 'of', 'words', 'that', 'make', 'little', 'actual', 'sense']
>>> long_list[7]
'words'
>>> long_list[7:9]
['words', 'that']
>>> long_list[7:]
['words', 'that', 'make', 'little', 'actual', 'sense']
>>> long_list[:7]
['I', 'am', 'a', 'very', 'long', 'list', 'of']
>>> long_list[-1]
'sense'
>>> long_list[0]
'I'
>>>
```
⛑ **In programming we start counting at 0 because 0 and nothing are not the same thing.**
🏃 Try it
---
Slice and dice away!
A handy method of the `str` class is `split` which will cut up a string into separate elements.
The result will be a `list` on which you can use `list` methods.
# For loop
TODO say hello to my friends exercise
TODO simple ROT13 cryptography with multiple libs
I mentioned the for loop, which is a loop in space, when we saw the `while` loop.
The [keyword](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) in question is, surprise surprise, `for`!
I see it as a loop in space because it will run *for each element in a sequence* which in my mind is something of substance.
Your logical mileage may vary but as long as you understand the following code block we're good.
```python3
import time
friends = ["max", "mike", "alice", "steve", "rosa", "hans"]
print("The door opens and in walk my {} friends!".format(len(friends)))
for friend in friends:
print("Hello {}!".format(friend.capitalize()))
time.sleep(1)
print("{} closes the door behind him...".format(friend.capitalize()))
```
# Coding challenge - Cheerleader chant
TODO nested for loop exercise
Can you make me a program that outputs this type of cheerleader chant?
You can make it *prettier* by importing your `pretty_print` function, plus you can add some `time.sleep` in it to make it more *musical*.
```
Give me an m
M
Give me an a
A
Give me an x
X
Gooooooooo, MAX!
Give me an m
M
Give me an i
I
Give me an k
K
Give me an e
E
Gooooooooo, MIKE!
Give me an c
C
Give me an a
A
Give me an m
M
Give me an i
I
Give me an l
L
Give me an l
L
Give me an e
E
Gooooooooo, CAMILLE!
```
<details>
<summary>Spoiler warning</summary>
```python3
friends = ["max", "mike", "camille"]
for friend in friends:
for letter in friend:
print("Give me an {}".format(letter))
print("{}".format(letter.upper()))
print("Gooooooooo, {}!".format(friend.upper()))
```
</details>
# Coding challenge - ROT13
ROT13 is one of the oldest cryptographic cyphers know to mankind.
It dates back to the Roman empire and is also known as a [Caesar cypher](https://en.wikipedia.org/wiki/Caesar_cipher).
The algorithm is pretty simple, you just shift a letter 13 places in the alphabet so `a` becomes `n` or `x` becomes `k`.
Have a look at [this](https://rot13.com/) website to see the cypher in action.
Now, can you make a program that encrypts a phrase with ROT13?
Something along these lines:
```python3
What's your secret? hello world!
encoded secret: uryyb jbeyq!
```
<details>
<summary>Spoiler warning</summary>
```python3
import string
def encode_rot(msg, rot=13):
msg = msg.lower()
letters = list(string.ascii_lowercase)
coded_msg = []
for letter in msg:
if letter not in letters:
coded_msg.append(letter)
else:
idx = letters.index(letter) + rot
coded_letter = letters[idx % len(letters)]
coded_msg.append(coded_letter)
coded_msg = "".join(coded_msg)
return coded_msg
def decode_rot(msg, rot=13):
pass
if __name__ == "__main__":
clear_message = input("What's your secret? ")
encoded_message = encode_rot(clear_message)
print("encoded secret: {}".format(encoded_message))
```
</details>
🏃 Try it
---
To make things more interesting you can add a decode function.
Plus you could add a prompt that asks how big the shift should be (ROT13, ROT16, ...).
# List comprehension
This is a bit of an advanced topic but I'm putting it here to show you a very unique and powerful feature of python.
It's an *in-line* combination of `list`, `for` and conditional logic which allows us to make lists of lists which obey certain conditions.
You can [learn](https://12ft.io/proxy?q=https%3A%2F%2Frealpython.com%2Flist-comprehension-python%2F) about it online.
```python3
mixed_list = ["one", "2", "three", "4", "5", "six6", "se7en", "8"]
digits = [d for d in mixed_list if d.isdigit()]
print(digits)
```
The code above can be recreated **without** list comprehension as well, it will just be *a lot* longer.
```python3
mixed_list = ["one", "2", "three", "4", "5", "six6", "se7en", "8"]
digits = []
for d in mixed_list:
if d.isdigit():
digits.append(d)
print(digits)
```
As the `d` in the list comprehension is **always** a digit, we can convert it to an integer on the spot!
```python3
mixed_list = ["one", "2", "three", "4", "5", "six6", "se7en", "8"]
digits = [int(d) for d in mixed_list if d.isdigit()]
print(digits)
```
# Handling files
When we `import` a library python will read and execute the file in question.
We can also just *read* a simple text file and *use* the data that's in the file.
There are two ways of reading a file, one more *pythonic* and one more linear.
I'll outline both and you can use whichever seems more logical to you.
## Reading from a file
### In-line way
The builtin function `open` is what's new here.
It takes two arguments, one is the [path](https://en.wikipedia.org/wiki/Path_(computing)) and the other is a *mode*.
The most used modes are **read** or **write**, and this as either **text** or **binary**.
Have a look at the documentation to discover more modes.
```python3
fp = open("./examples/data.txt", "r")
data = fp.readlines()
print("file contains: {}".format(data))
fp.close()
```
### Pythonic way
The *exact* same thing can be done in a more pythonic way as follows.
The beauty of the `with` syntax is that the `close` function call is implied by the indentation.
I personally prefer this way of reading and writing files but you do you!
```python3
file_to_open = "./examples/data.txt"
with open(file_to_open, "r") as fp:
data = fp.readlines()
print("file contains: {}".format(data))
print("{} has {} lines".format(file_to_open, len(data)))
```
## Writing to a file
## csv, JSON and yaml
Writing to a file can also be done in two ways, a pythonic and *less* pythonic way.
As I prefer the pythonic way I'll only showcase that one but you'll be able to easily adapt the code yourself.
The only difference is the **mode** we use to `open` the file.
## pickle
```python3
first_name = input("what's your first name? ")
last_name = input("what's your last name? ")
birthday = input("what's your date of birth? ")
data = [first_name, last_name, birthday]
file_to_open = "./examples/test.tmp"
with open(file_to_open, "w") as fp:
for element in data:
fp.write("{}\n".format(element))
print("done!")
```
There is also a way to write a batch of lines to a file in one go.
This is done as follows.
But, you'll notice there is no **newline** added after each element.
```python3
data = ["wouter", "gordts", "1986"]
file_to_open = "./examples/test.tmp"
with open(file_to_open, "w") as fp:
fp.writelines(data)
```
# Coding challenge - Login generator
TODO write a login generator as a library with a cli as program
BONUS argparse, save to file, read from file
Can you write me a program that creates random, but easy to remember, usernames?
Plus, for each username also a *not-so-easy-to-remember* password?
Maybe with a bit of flexibility?
For example variable password length?
This is an exercise you can take pretty far if you plan it out properly.
I would advise you to write the **generator** functions as a library.
Then import those functions into the program where you implement the user facing logic.
By doing so you can reuse your code for multiple interfaces (CLI, TUI, [argparse](https://docs.python.org/3/howto/argparse.html)).
If you want to you can also **save** your logins to a file!
An example of the output I expect:
```
how many login pairs would you like to create?3
you want complex passwords? (y/n)y
how long should the password be?32
username 0: EarnestCrocodile
password 0: :sdGV&[FDYZZ|RXUpZeo`J&t@*Z>^fEW
username 1: AbstractedDragon
password 1: 32hz5&C@<o\OMa9tnET(lk(3wF%d?$Dy
username 2: MeekStallion
password 2: +;^di8a":AD;_b4^w$Fj'RVkI`CoG,LX
```
<details>
<summary>Spoiler warning</summary>
This spoiler warning is in multiple steps.
If you're unsure how to generate the *random-yet-easy-to-remember* have a look at [this file](./assets/subjects.txt) and [this file](./assets/adjectives.txt).
**Stop here and try it out!**
If you're unsure how to tackle the *library part* of this exercise, have a look at [this file](./assets/login_generator.py).
Don't just copy this code, read it and recreate it yourself!
**Stop here and try it out!**
Once the library is done you can code the *interface*.
For some inspiration for a simple question/response system you can have a look [here](./assets/pwd_cli.py).
</details>
# Dictionaries as data containers
TODO adapt the login generator to output a dict
Of the *built-in* types we first say `str`, `int` and `float`.
Next we saw *sequence* types such as `list` and `tupple`.
Now we'll dive into a **mapping type** called `dict`.
I advise you to have a look at the [reference pages](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict) when in doubt.
# Creating our own classes
A dictionary is *kind* of like a list but it has **two** objects per **element**.
We call them **key** and **value**.
There are a couple of rules you need to be aware of though.
## Class examples
1. In a `dict` the keys have to be **unique**.
2. A dictionary is **unordered** meaning the *first* element is not garanteed to remain the first over the lifespan of the dictionary.
3. The keys used must be **hashable**.
Let's visualize a legal dictionary!
It's declared with **curly brackets** as follows `{}`.
```python3
my_data = {"key": "value", "name": "wouter", "age": 35}
same_data_different_layout = {
"key": "value",
"name": "wouter",
"age": 35,
}
```
Let's have a look at the **methods** we can invoke on a `dict`.
```python3
>>> my_data = {"key": "value", "name": "wouter", "age": 35,}
>>> my_data.
my_data.clear( my_data.get( my_data.pop( my_data.update(
my_data.copy( my_data.items( my_data.popitem( my_data.values(
my_data.fromkeys( my_data.keys( my_data.setdefault(
>>> my_data.keys()
dict_keys(['key', 'name', 'age'])
>>> my_data.values()
dict_values(['value', 'wouter', 35])
>>> my_data.items()
dict_items([('key', 'value'), ('name', 'wouter'), ('age', 35)])
>>> for key, value in my_data.items():
... print("key is {}".format(key))
... print("value is {}".format(value))
...
key is key
value is value
key is name
value is wouter
key is age
value is 35
>>>
```
We can reference specific **values** corresponding to specific **keys** as follows.
```pythons
>>> my_data["name"]
'wouter'
>>> my_data["age"]
35
>>>
```
We can use dictionaries as data containers and put them in a list to group together similar data elements.
The code below should explain it quite nicely.
```python3
login_ovh = {"username": "EarnestCrocodile", "password": ":sdGV&[FDYZZ|RXUpZeo`J&t@*Z>^fEW"}
login_mailbox = {"username": "AbstractedDragon", "password": "32hz5&C@<o\OMa9tnET(lk(3wF%d?$Dy"}
login_gitea = {"username": "MeekStallion", "password": "+;^di8af:AD;_b4^w$Fj'RVkI`CoG,LX"}
my_login_list = [login_ovh, login_mailbox, login_gitea]
for login in my_login_list:
print("login {}".format(my_login_list.index(login)))
for key in login.keys():
print("\t{}: {}".format(key, login[key]))
```
# Coding challenge - Task manager
Can you create me a *task manager* please?
I made one that is run from the command line with different arguments to modify it's behaviour.
If this looks too challenging I can tell you that the *full code* is 64 lines long, including empty lines!
Those of you who already tried out [argparse](https://realpython.com/command-line-interfaces-python-argparse/) in the previous challenges will probably understand what's going on here.
Those who did not I urge you to have a look at the link above and don't hesitate to ask for help!
```bash
➜ python_course_doc git:(master) ✗ python3 test.py --help
usage: test.py [-h] [--file FILE] {add,delete,show} ...
positional arguments:
{add,delete,show}
add adds a todo item to you todo list
delete deletes an item from your todo list
show show all your tasks
optional arguments:
-h, --help show this help message and exit
--file FILE, -f FILE path to your todo file
➜ python_course_doc git:(master) ✗ python3 test.py show
ID: 0 --- buy milk
ID: 1 --- clean house
ID: 2 --- test my code
➜ python_course_doc git:(master) ✗ python3 test.py add write some documentation
➜ python_course_doc git:(master) ✗ python3 test.py show
ID: 0 --- buy milk
ID: 1 --- clean house
ID: 2 --- test my code
ID: 3 --- write some documentation
➜ python_course_doc git:(master) ✗ python3 test.py delete 2
➜ python_course_doc git:(master) ✗ python3 test.py show
ID: 0 --- buy milk
ID: 1 --- clean house
ID: 2 --- write some documentation
➜ python_course_doc git:(master) ✗
```
<details>
<summary>Spoiler warning</summary>
```python3
import argparse
import pathlib
def read_taskfile(taskfile_path):
tasks = []
if not pathlib.Path(taskfile_path).exists():
return tasks
with open(taskfile_path, "r") as fp:
lines = fp.readlines()
for line in lines:
task = line.strip()
tasks.append(task)
return tasks
def write_taskfile(taskfile_path, tasks):
with open(taskfile_path, "w") as fp:
for task in tasks:
fp.write("{}\n".format(task))
def show_tasks(tasks):
counter = 0
for task in tasks:
print("ID: {} --- {}".format(counter, task))
counter += 1
def add_task(tasks, task):
tasks.append(task)
return tasks
def delete_task(tasks, task_id):
tasks.pop(task_id)
return tasks
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--file", "-f", default="./todo.tasks", help="path to your todo file")
subparser = parser.add_subparsers()
add = subparser.add_parser("add", help="adds a todo item to you todo list")
add.add_argument("task", nargs="*")
delete = subparser.add_parser("delete", help="deletes an item from your todo list")
delete.add_argument("idx", type=int, nargs="*")
show = subparser.add_parser("show", help="show all your tasks")
show.add_argument("show", action="store_true")
args = parser.parse_args()
taskfile = args.file
tasks = read_taskfile(taskfile)
if "task" in args:
tasks = add_task(tasks, " ".join(args.task))
write_taskfile(taskfile, tasks)
elif "idx" in args:
for idx in args.idx:
tasks = delete_task(tasks, idx)
write_taskfile(taskfile, tasks)
elif args.show:
show_tasks(tasks)
```
</details>
# Text based databases
The todo list example from before is handy but quite *limited* as a database.
As is it only holds one form or information and that is the *actual task*.
What if we want to add urgency or mark tasks complete (instead of deleting)?
This can be done by grouping data together.
We already saw a dictionaries which are good mapping structures but how can we save them to disk?
A hacky way would be to write a python file containing the `dict` and `import` it when we need it.
But there are better ways.
# Now for some useful scripting
With everything we have learned up until now you can start doing some interesting and useful things.
Have a look at [these exercises](https://gitea.86thumbs.net/waldek/linux_course_doc/src/branch/master/modules/qualifying/exercise_python.md).
You can try them out at your own pace.
I can give some hints or pointers if needed, either in class or individually.
Don't hesitate to ask for help!
# Creating our own objects
## First some *abstract* examples
TODO simple animal or vehicle exercise
TODO task manager
## Class inheritance
TODO shapes and surfaces
TODO superhero game
## Improve the login generator
## Now some *practical* improvements
### Improve the login generator
TODO convert the login generator to a class
### Improve the task manager
TODO convert the task manager to a class
# Infinite programs
* insist on the nature of scripts we did up until now
## Logic breakdown of a simple game
TODO hangman exercise
```
***********************
* welcome to hangman! *
***********************
guess the hidden word below
---------------------------
word: *****
guess a letter: a
word: a****
guess a letter: l
word: a**l*
guess a letter: p
word: appl*
guess a letter: e
word: apple
*****************
* you found it! *
*****************
do you want to play a new game? (Y/N)
```
## Trivial pursuit multiple choice game