1311 lines
41 KiB
Markdown
1311 lines
41 KiB
Markdown
# What we'll learn
|
|
|
|
You'll learn **three** things at the same time so don't get discouraged if it feels a bit much at the start.
|
|
Everybody's issues will be in these three different domains and at the beginning it can be difficult to differentiate between them.
|
|
Keep this in mind, everybody has to go through this stage and the *click* comes at different times for different people but everybody clicks at some point!
|
|
The three new things you'll learn:
|
|
|
|
1. the **concepts** of programming, most notably Object Orientated Programming (OOP)
|
|
2. the **syntax** of one particular language, in our case Python3
|
|
3. the **tools** needed to start programming in our language of choice
|
|
|
|
Within each of these topics there are *subtopics* but there are not bottomless!
|
|
Below is a small overview of how I would subdivide them.
|
|
|
|
## Concepts
|
|
|
|
The subtopics behind the concept of programming can be sliced (in no particular order) as follows:
|
|
|
|
* objects or **OOP** (Object Orientated Programming)
|
|
* abstraction
|
|
* encapsulation
|
|
* inheritance
|
|
* polymorphism
|
|
* variables (which are **not** *boxes* in python3)
|
|
* conditional logic
|
|
* functions
|
|
* loops
|
|
|
|
The concept behind these topics are the same in most languages, it's just *how* you write them that is different.
|
|
This *how* is part of the **syntax** of the language.
|
|
|
|
## Syntax
|
|
|
|
> In computer science, the syntax of a computer language is the set of rules that defines the combinations of symbols that are considered to be correctly structured statements or expressions in that language.
|
|
> This applies both to programming languages, where the document represents source code, and to markup languages, where the document represents data.
|
|
> The syntax of a language defines its surface form.[1] Text-based computer languages are based on sequences of characters,
|
|
|
|
The quote above is taken shamelessly from [wikipedia](https://en.wikipedia.org/wiki/Syntax_(programming_languages)).
|
|
|
|
## Tools
|
|
|
|
### Writing code
|
|
|
|
Scripts are text files, plain and simple.
|
|
So in order to **write** a Python3 script all we need is a text editor.
|
|
Nano, vim, notepad++ all do a good job of editing plain text files but some make it *easier* than others.
|
|
You've noticed that vim colors the code of a shell script no?
|
|
One of the many features of an [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) is *syntax highlighting*.
|
|
It colors things such as [keywords](https://realpython.com/python-keywords/) which makes our life so much nicer when writing code.
|
|
We'll come back to these features in a bit.
|
|
|
|
### Running code
|
|
|
|
In order to **run** Python3 code you need the Python3 interpreter.
|
|
This is because when you **execute** your script, the interpreter will **read and execute** each line of the text file **line by line**.
|
|
|
|
Most people who want to write and run Python3 code, or any language for that matter, will install an Integrated Development Environment to do so.
|
|
There are no *rules* as to what has to be included for a program to qualify as an IDE but in my opinion they should include:
|
|
|
|
* syntax highlighting
|
|
* autocomplete
|
|
* *goto* commands such as goto definition, goto declaration, goto references
|
|
* automatic *pair* opening and closing
|
|
* builtin help navigation
|
|
|
|
There is a plethora of IDE's available and you can't really make a *wrong* choice here, but to make the overall learning curve a bit less steep we'll start out with a user friendly IDE, [pycharm](https://www.jetbrains.com/help/pycharm/installation-guide.html).
|
|
|
|
# The python3 shell
|
|
|
|
TODO animated overview of the shell and the world of OOP
|
|
|
|
|
|
# Installing pycharm
|
|
|
|
TODO
|
|
|
|
# Your first project
|
|
|
|
In almost any language you'll find a *helloworld* program.
|
|
It serves to illustrate a *very* basic working script or program to showcase the syntax.
|
|
In python a helloworld is done as such.
|
|
|
|
```python3
|
|
print("Hello World!")
|
|
```
|
|
|
|
Just for reference below are a few helloworld programs in different languages.
|
|
First `c#` then `c` then `c++` and last but not least `javascript`.
|
|
|
|
### c#
|
|
|
|
```c#
|
|
Console.WriteLine("Hello World!");
|
|
```
|
|
|
|
### c
|
|
|
|
```c
|
|
#include <stdio.h>
|
|
|
|
int main() {
|
|
printf("Hello World!");
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### c++
|
|
|
|
```c++
|
|
// Your First C++ Program
|
|
|
|
#include <iostream>
|
|
|
|
int main() {
|
|
std::cout << "Hello World!";
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### javascript
|
|
|
|
```javascript
|
|
alert( 'Hello, world!' );
|
|
```
|
|
|
|
## How to execute
|
|
|
|
* within pycharm
|
|
* from the command line
|
|
|
|
## Simple printing
|
|
|
|
The most basic printing can be done by calling the `print` function.
|
|
In python a call is symbolized by the `()`.
|
|
In practice this becomes as follows.
|
|
|
|
```python3
|
|
print("hello world")
|
|
print("my name is Wouter")
|
|
print("I'm", 35, "years old")
|
|
```
|
|
|
|
🏃 Try it
|
|
---
|
|
|
|
Try printing different lines and with combinations of different object types such as `int`, `float` and `str`.
|
|
What happens if you *add* (`+`) values to one another?
|
|
|
|
|
|
We can also print the objects referenced by variables.
|
|
A simple example:
|
|
|
|
```python3
|
|
name = "Wouter"
|
|
age = "35"
|
|
|
|
print("Hello, my name is", name, "and I'm", age, "years old.")
|
|
```
|
|
|
|
While it works perfectly well it's not super *readable*.
|
|
We can improve the readability by using either string replacement or string formatting.
|
|
My personal preference is string formatting.
|
|
|
|
🏃 Try it
|
|
---
|
|
|
|
Have a look at both ways illustrated below and try them out.
|
|
|
|
|
|
## String replacement
|
|
|
|
```python3
|
|
name = "Wouter"
|
|
age = "35"
|
|
|
|
print(f"Hello, my name is {name} and I'm {age} years old.")
|
|
```
|
|
|
|
## String formatting
|
|
|
|
```python3
|
|
name = "Wouter"
|
|
age = "35"
|
|
|
|
print("Hello, my name is {} and I'm {} years old.".format(name, age))
|
|
```
|
|
|
|
## Some links to read up
|
|
|
|
* [realpython string formatting](https://realpython.com/python-string-formatting/)
|
|
* [realpython data types](https://realpython.com/python-data-types/)
|
|
|
|
# Taking input
|
|
|
|
The first **builtin function** we saw is `print` which can be used to signal messages to the user.
|
|
But how can we **get** some information from the user?
|
|
This is done with the `input` function.
|
|
If we open up a python shell we can observe it's behaviour.
|
|
|
|
```python3
|
|
>>> input()
|
|
hello world
|
|
'hello world'
|
|
>>>
|
|
```
|
|
|
|
It seems to echo back what we type on the empty line.
|
|
If we take this idea and add it to a script the behaviour changes slightly.
|
|
The [prompt](https://en.wikipedia.org/wiki/Command-line_interface#Command_prompt) appears but when we hit `enter` the text is not printed.
|
|
This is one of the slight nuances between running scripts and using the shell.
|
|
The shell is more *verbose* and will explicitly tell you what a function returns, unless it doesn't return anything.
|
|
|
|
## Some functions are blocking
|
|
|
|
When we call `print` the function is executed immediately and the python interpreter continues with the next line.
|
|
The `input` function is slightly different.
|
|
It is called a **blocking** function.
|
|
When a blocking function is called the program is *waiting* for some action.
|
|
Once the condition is met, the program continues.
|
|
It's important to be aware of this but don't overthink it.
|
|
We'll get back to this behaviour later.
|
|
|
|
## Functions can return something
|
|
|
|
So, functions can **return** something but how can we *use* the returned objects?
|
|
This is where **variables** come in handy.
|
|
The `input` function will **always** return an object of type `str`.
|
|
If we want to use this object later in our code we need to add a *post-it* to it so we can reference it later.
|
|
Remember that the object is created by the function call, and we add the reference after the object's creation.
|
|
|
|
```python3
|
|
print("What is your name? ")
|
|
answer = input()
|
|
print("Well hello", answer, "!")
|
|
```
|
|
|
|
**When looking at the code block above did you notice the *empty space* I added after my question?
|
|
Can you tell me why I did that?**
|
|
|
|
🏃 Try it
|
|
---
|
|
|
|
Try playing around with the `input` function and incorporate the different ways to print with it.
|
|
Ask multiple questions and combine the answers to print on one line.
|
|
|
|
## Functions can take arguments
|
|
|
|
Some, if not most, functions will take one or more arguments when calling them.
|
|
This might sound complicated but you've already done this!
|
|
The `print` function takes *a-message-to-print* as an argument, or even multiple ones as you probably noticed when playing around.
|
|
|
|
The `input` function *can* take arguments but as we've seen does not *require* an argument.
|
|
When looking at the documentation we can discover **what** the function does, how to **call** the function and what it **returns**.
|
|
|
|
⛑ **CTRL-q opens the documentation in pycharm**
|
|
|
|
```
|
|
Help on built-in function input in module builtins:
|
|
|
|
input(prompt=None, /)
|
|
Read a string from standard input. The trailing newline is stripped.
|
|
|
|
The prompt string, if given, is printed to standard output without a
|
|
trailing newline before reading input.
|
|
|
|
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
|
|
On *nix systems, readline is used if available.
|
|
```
|
|
|
|
We can add one **argument** inside the `input` call which serves as a prompt.
|
|
Now which `type` should the object we pass to `input` be?
|
|
The most logical type would be a `str` that represents the *question* to ask the user no?
|
|
Let's try it out.
|
|
|
|
```python3
|
|
answer = input("What is your name?")
|
|
print("Well hello {}!".format(answer))
|
|
```
|
|
|
|
🏃 Try it
|
|
---
|
|
|
|
Modify the questions you asked before so they have a proper prompt via the `input` function.
|
|
Ask multiple questions and combine the answers to print on one line with different `print` formatting.
|
|
|
|
# Taking input and evaluation
|
|
|
|
We can do a lot more with the input from users than just print it back out.
|
|
It can be used to make logical choices based on the return **value**.
|
|
This is a lot easier than you think.
|
|
Imagine you ask me my age.
|
|
When I respond with *35* you'll think to yourself *"that's old!"*.
|
|
If I would be younger then *30* you would think I'm young.
|
|
We can implement this logic in python with easy to read syntax.
|
|
First we'll take a python **shell** to experiment a bit.
|
|
|
|
```python3
|
|
>>> 35 > 30
|
|
True
|
|
>>> 25 > 30
|
|
False
|
|
>>> 30 > 30
|
|
False
|
|
>>> 30 >= 30
|
|
True
|
|
>>>
|
|
```
|
|
|
|
The `True` and `False` you see are also objects but of the type `bool`.
|
|
If you want to read up a bit more on boolean logic I can advise you [this](https://en.wikipedia.org/wiki/Boolean_algebra) page and also [this](https://realpython.com/lessons/boolean-logic/).
|
|
This boolean logic open the door towards **conditional logic**.
|
|
|
|
## Conditional logic
|
|
|
|
Let's convert the quote below to logical statements.
|
|
|
|
> If you are younger than 27 you are still young so if you're older than 27 you're considered old, but if you are 27 on the dot your life might be at [risk](https://en.wikipedia.org/wiki/27_Club)!
|
|
|
|
```python3
|
|
age = 35
|
|
if age < 27:
|
|
print("you are still very young, enjoy it!")
|
|
elif age > 27:
|
|
print("watch out for those hips oldie...")
|
|
else:
|
|
print("you are at a dangerous crossroad in life!")
|
|
```
|
|
|
|
⛑ **Do not fight the automatic indentation in your IDE! Pycharm is intelligent enough to know when to indent so if it does not indent by itself, you probably made a syntax error.**
|
|
|
|
## Class string methods
|
|
|
|
Let's take the logic above and implement it in a real program.
|
|
I would like the program to ask the user for his/her name and age.
|
|
After both questions are asked I would like the program to show a personalized message to the user.
|
|
The code below is functional but requires quite a bit of explaining!
|
|
|
|
```python3
|
|
name = input("What is you name?")
|
|
age = input("What is you age?")
|
|
|
|
if age.isdigit():
|
|
age = int(age)
|
|
else:
|
|
print("{} is not a valid age".format(age))
|
|
exit(1)
|
|
|
|
if age < 27:
|
|
print("My god {}, you are still very young, enjoy it!".format(name))
|
|
elif age > 27:
|
|
print("Wow {}! Watch out for those hips oldie...".format(name))
|
|
else:
|
|
print("{}, you are at a dangerous crossroad in life!".format(name.capitalize()))
|
|
```
|
|
|
|
An object of the type `str` has multiple **methods** that can be applied on itself.
|
|
It might not have been obvious but the *string formatting* via `"hello {}".format("world")` we did above is exactly this.
|
|
We **call** the `.format` **method** on a `str` object.
|
|
Strings have a handful of methods we can call.
|
|
Pycharm lists these methods when you type `.` after a string.
|
|
In the **shell** we can visualize this as well.
|
|
For those of you not familiar with shells have a look at [tab completion](https://en.wikipedia.org/wiki/Command-line_completion).
|
|
|
|
```python3
|
|
>>> name = "wouter"
|
|
>>> name.
|
|
name.capitalize( name.format( name.isidentifier( name.ljust( name.rfind( name.startswith(
|
|
name.casefold( name.format_map( name.islower( name.lower( name.rindex( name.strip(
|
|
name.center( name.index( name.isnumeric( name.lstrip( name.rjust( name.swapcase(
|
|
name.count( name.isalnum( name.isprintable( name.maketrans( name.rpartition( name.title(
|
|
name.encode( name.isalpha( name.isspace( name.partition( name.rsplit( name.translate(
|
|
name.endswith( name.isascii( name.istitle( name.removeprefix( name.rstrip( name.upper(
|
|
name.expandtabs( name.isdecimal( name.isupper( name.removesuffix( name.split( name.zfill(
|
|
name.find( name.isdigit( name.join( name.replace( name.splitlines(
|
|
>>> name.capitalize()
|
|
'Wouter'
|
|
>>> name.isd
|
|
name.isdecimal( name.isdigit(
|
|
>>> name.isdigit()
|
|
False
|
|
>>> age = "35"
|
|
>>> age.isdigit()
|
|
True
|
|
>>>
|
|
```
|
|
|
|
⛑ **Remember CTRL-q opens the documentation in Pycharm and don't forget to actually use it!**
|
|
|
|
## Some links to read up on
|
|
|
|
* [realpython](https://realpython.com/python-conditional-statements/) conditional logic
|
|
|
|
# Coding challenge - Celsius to Fahrenheit converter
|
|
|
|
Your first challenge!
|
|
I would like you to write a program that converts Celsius to Fahrenheit.
|
|
You should do this in a **new** python file.
|
|
I suggest you call it `c_to_f.py` or something that makes sense to you.
|
|
The result of this program *could* be as follows.
|
|
|
|
```bash
|
|
➜ ~ git:(master) ✗ python3 ex_celcius_to_fahrenheit.py
|
|
What's the temperature?30
|
|
30°C equals 86.0°F
|
|
Go turn off the heating!
|
|
➜ ~ git:(master) ✗ python3 ex_celcius_to_fahrenheit.py
|
|
What's the temperature?4
|
|
4°C equals 39.2°F
|
|
Brrrr, that's cold!
|
|
➜ ~ git:(master) ✗ python3 ex_celcius_to_fahrenheit.py
|
|
What's the temperature?blabla
|
|
I can't understand you...
|
|
➜ ~ git:(master) ✗
|
|
```
|
|
|
|
If you want to make the program a bit more complex, try adding the reverse as in Fahrenheit to Celsius.
|
|
Your first question to the user could then be *in which direction do you want to convert?*.
|
|
|
|
<details>
|
|
<summary>Spoiler warning</summary>
|
|
|
|
```python3
|
|
result = input("What's the temperature?")
|
|
|
|
if result.isdigit():
|
|
celsius = int(result)
|
|
else:
|
|
print("I can't understand you...")
|
|
exit(1)
|
|
|
|
farenheit = celsius * (9/5) + 32
|
|
|
|
print("{}°C equals {}°F".format(celsius, farenheit))
|
|
|
|
if celsius < 15:
|
|
print("Brrrr, that's cold!")
|
|
else:
|
|
print("Go turn off the heating!")
|
|
|
|
```
|
|
|
|
</details>
|
|
|
|
# A text based adventure game
|
|
|
|
We can use conditional logic to create quite elaborate decision processes.
|
|
Let's build a mini text based adventure game.
|
|
Granted it's not a *tripple A* game but it will train your `if` and `else` skills plus it will highlight some issues we'll overcome in the next section.
|
|
|
|
![adventure game](./assets/text_based_adventure_game.png)
|
|
|
|
Consider the diagram above we can imagine a program that functions nicely with the code below.
|
|
It is not very *readable* nor *scalable*.
|
|
|
|
```python3
|
|
answer = input("You're at a cross section. Do you go left or right?")
|
|
if answer.startswith("l"):
|
|
answer = input("Down this hall you encounter a bear. Do you fight it?")
|
|
if answer.startswith("y"):
|
|
print("The bear counter attack! He kills you")
|
|
print("game over!")
|
|
exit(0)
|
|
elif answer.startswith("n"):
|
|
print("It's a friendly bear! He transforms into a wizard!")
|
|
answer = input("The wizard asks you if you know the meaning of life?")
|
|
if answer == "42":
|
|
print("He knods approuvingly and upgrades you to wizard status!")
|
|
print("You win!")
|
|
exit(0)
|
|
else:
|
|
print("He shakes his head in disbelief. You fool!")
|
|
print("game over!")
|
|
else:
|
|
print("that's not a valid choice...")
|
|
print("game over!")
|
|
exit(0)
|
|
elif answer.startswith("r"):
|
|
answer = input("Down this hall you find some mushrooms. Do you eat them?")
|
|
if answer.startswith("n"):
|
|
print("You starve to dead...")
|
|
print("game over!")
|
|
exit(0)
|
|
elif answer.startswith("y"):
|
|
print("A wizard apprears out of thin air!")
|
|
answer = input("The wizard asks you if you know the meaning of life?")
|
|
if answer == "42":
|
|
print("He knods approuvingly and upgrades you to wizard status!")
|
|
print("You win!")
|
|
exit(0)
|
|
else:
|
|
print("He shakes his head in disbelief. You fool!")
|
|
print("game over!")
|
|
else:
|
|
print("that's not a valid choice...")
|
|
print("game over!")
|
|
exit(0)
|
|
pass
|
|
else:
|
|
print("game over!")
|
|
exit(0)
|
|
```
|
|
|
|
I urge you to read up on some [best practices](https://www.tutorialdocs.com/article/python-conditional-statements-tips.html) for `if` statements.
|
|
We will not improve on this particular example but I do advise you to create a similar style game during one of the workshops once we have learned some new tricks.
|
|
|
|
# Creating your own functions
|
|
|
|
One of the *issues* we have in the text based game example is duplicate code.
|
|
At two spots we execute almost identical code and this is something that should be avoided at all costs!
|
|
Why write the same thing over and over?
|
|
You're better off writing it **once** and use it **lots**.
|
|
This is where **functions** come into play.
|
|
|
|
Python ships with a lot of built-in functions but we can create our own very easily.
|
|
The keyword to define a function in python is `def`.
|
|
All the code that is *indented* will be executed when we **call** the function.
|
|
Here is a basic abstraction of correct syntax.
|
|
|
|
```python3
|
|
def first_function():
|
|
print("I'm a function")
|
|
print("Hear me roar!")
|
|
```
|
|
|
|
If you type *only* the code above in a new script, and run it, you won't see much.
|
|
This is because you only **created** the function.
|
|
To use it you need to **call** it.
|
|
This is done as follows.
|
|
|
|
```python3
|
|
def first_function():
|
|
print("I'm a function")
|
|
print("Hear me roar!")
|
|
|
|
first_function()
|
|
```
|
|
|
|
Learning how to create functions is a big step in your programming journey.
|
|
It can seem confusing at first because the code execution *appears* to jump around.
|
|
This is however **not** the case.
|
|
Your script is still read and executed **line by line** so you can not call a function **before** you defined it!
|
|
For now you should not overthink the structure of you scripts.
|
|
As long as they work you should be happy.
|
|
We'll dive into the proper *anatomy* of a program real soon.
|
|
|
|
## Functions that *do* something
|
|
|
|
The first function I showed you above performs a series of actions each time it is called.
|
|
We can use it to *bake cakes* for example.
|
|
Below we create **one** function to bake the cake, and call it **three** times to bake three cakes.
|
|
|
|
```python3
|
|
def bake_chocolate_cake():
|
|
print("mix the base ingredients")
|
|
print("add the chocolate flavour")
|
|
print("put in the oven")
|
|
print("enjoy!")
|
|
|
|
bake_chocolate_cake()
|
|
bake_chocolate_cake()
|
|
bake_chocolate_cake()
|
|
```
|
|
|
|
Now, you might like a *vanilla* cake from time to time.
|
|
Easy, we'll just write a second function for that purpose.
|
|
|
|
```python3
|
|
def bake_chocolate_cake():
|
|
print("mix the base ingredients")
|
|
print("add the chocolate flavour")
|
|
print("put in the oven")
|
|
print("enjoy!")
|
|
|
|
def bake_vanilla_cake():
|
|
print("mix the base ingredients")
|
|
print("add the vanilla flavour")
|
|
print("put in the oven")
|
|
print("enjoy!")
|
|
|
|
bake_chocolate_cake()
|
|
bake_chocolate_cake()
|
|
bake_vanilla_cake()
|
|
bake_chocolate_cake()
|
|
bake_vanilla_cake()
|
|
```
|
|
|
|
Voila, we can now make as many chocolate and vanilla cakes as we want!
|
|
But what about *bananas*?
|
|
Following our logic we can create a *third* function to bake a banana cake but you're probably seeing a *pattern* here.
|
|
Each `bake_FLAVOR_cake` function is almost identical, just for the flavoring.
|
|
We can create one generic `bake_cake` function and just add the custom flavor each time we actually *bake* a cake.
|
|
This is done with arguments.
|
|
|
|
```python3
|
|
def bake_cake(flavor):
|
|
print("mix the base ingredients")
|
|
print("add the {} flavor".format(flavor))
|
|
print("put in the oven")
|
|
print("enjoy!")
|
|
|
|
bake_cake("chocolate")
|
|
bake_cake("vanilla")
|
|
bake_cake("banana")
|
|
```
|
|
|
|
## Variable scope
|
|
|
|
Variable scope might sounds complicated but with some examples you'll understand it in no time.
|
|
Remember variables are like unique *post-its*?
|
|
Well, variable scope is like having multiple colors for your post-its.
|
|
A yellow post-it with `name` on it is not the same as a red one with `name` on it so they can both reference **different** objects.
|
|
Scope is **where** the colors change, and this is done automatically for you in python.
|
|
It's an inherent feature of the language but the concept of scope is not unique to python, you'll find it in most modern languages.
|
|
Now, some examples.
|
|
|
|
```python3
|
|
total = 9000
|
|
|
|
def function_scope(argument_one, argument_two):
|
|
print("inside the function", argument_one, argument_two)
|
|
total = argument_one + argument_two
|
|
print("inside the function", total)
|
|
|
|
|
|
function_scope(300, 400)
|
|
print("outside the function", total)
|
|
```
|
|
|
|
Here `total` outside of the function references a different object from the `total` inside of the function.
|
|
Python is very *nice* and will try to fix some common mistakes or oversights by itself.
|
|
For example.
|
|
|
|
```python3
|
|
name = "Wouter"
|
|
|
|
def function_scope():
|
|
print("inside the function", name)
|
|
|
|
|
|
function_scope()
|
|
print("outside the function", name)
|
|
```
|
|
|
|
But we can not **modify** the referenced object from **inside** the function.
|
|
This will give an `UnboundLocalError: local variable 'name' referenced before assignment` error
|
|
|
|
```python3
|
|
name = "Wouter"
|
|
|
|
def function_scope():
|
|
print("inside the function", name)
|
|
name = "Alice"
|
|
|
|
|
|
function_scope()
|
|
print("outside the function", name)
|
|
```
|
|
|
|
There is however a handy **keyword** we can use to explicitly reference variables from the outermost scope.
|
|
This is done as follows with the `global` keyword.
|
|
|
|
```python3
|
|
name = "Wouter"
|
|
|
|
def function_scope():
|
|
global name
|
|
print("inside the function", name)
|
|
name = "Alice"
|
|
|
|
|
|
function_scope()
|
|
print("outside the function", name)
|
|
```
|
|
|
|
## Functions that *return* something
|
|
|
|
While the `global` keyword can be useful, it's rarely used.
|
|
This is because function can not only *do* thing, they can **return** objects.
|
|
The `input` function we've been using is a prime example of this as it always give you a `str` object back with the content of the user input.
|
|
We can copy this behaviour as follows.
|
|
|
|
```python3
|
|
def square_surface(length, width):
|
|
surface = length * width
|
|
return surface
|
|
|
|
|
|
square = square_surface(20, 40)
|
|
print(square)
|
|
```
|
|
|
|
🏃 Try it
|
|
---
|
|
|
|
Functions that return an object are essential to any modern programming language.
|
|
Think of some calculations such as the Celsius to Fahrenheit converter and create the corresponding functions.
|
|
|
|
## Some links to read up on
|
|
|
|
* some [best practices](https://able.bio/rhett/python-functions-and-best-practices--78aclaa) for functions
|
|
* more details on [variable scope](https://pythongeeks.org/python-variable-scope/)
|
|
* in depth [article](https://realpython.com/python-return-statement/) on return values in python
|
|
|
|
# Coding challenge - Pretty Print
|
|
|
|
Can you write me a function that decorates a name or message with a *pretty* character.
|
|
You should do this in a **new** python file.
|
|
I suggest you call it `pretty_frame.py` or something that makes sense to you.
|
|
Kind of like the two examples below.
|
|
|
|
```
|
|
##########
|
|
# Wouter #
|
|
##########
|
|
|
|
#################
|
|
# Python rules! #
|
|
#################
|
|
```
|
|
|
|
As an extra challenge you could return a [multi line string](https://www.askpython.com/python/string/python-multiline-strings) and print it outside of the function!
|
|
|
|
<details>
|
|
<summary>Spoiler warning</summary>
|
|
|
|
```python3
|
|
def pretty_print(msg, decorator="#"):
|
|
line_len = len(msg) + (len(decorator) * 2) + 2
|
|
print(decorator * line_len)
|
|
print("{} {} {}".format(decorator, msg, decorator))
|
|
print(decorator * line_len)
|
|
|
|
pretty_print("Wouter")
|
|
pretty_print("Python rules!")
|
|
pretty_print("Alice", "-")
|
|
```
|
|
|
|
</details>
|
|
|
|
# Using the standard library
|
|
|
|
There is no need to reinvent the wheel each time you build a bike.
|
|
The same goes for programming.
|
|
Most, if not all, programming languages come with a **standard library** which is a collection of additional objects and functions to facilitate common problems.
|
|
We'll look at some essential ones together but I urge you to [read](https://docs.python.org/3/library/index.html) up a bit when you have some free time.
|
|
Over the course of you programming journey you'll discover that efficient programming is often finding the right libraries and chaining them together to suit your use case.
|
|
|
|
Imagine we want to include a dice in our text based adventure game.
|
|
How on earth do we program that?
|
|
We need some for of *randomness* in our code.
|
|
A [quick google](https://stackoverflow.com/questions/28705965/python-random-function-without-using-random-module) demonstrates this is quite difficult without the use of libraries.
|
|
As randomness is both [extensively](https://www.random.org/randomness/) used in programming **and** it's quite [difficult](https://medium.com/swlh/random-functions-a4f36b1dfd8f) to do properly, you'll find a `random` library in most languages.
|
|
|
|
The new **keyword** you'll learn here is `import`.
|
|
It allows you to add extra functionality to your program.
|
|
Once *imported* we can **call** functions that exist from the library in question.
|
|
So, our dice becomes as follows.
|
|
|
|
```python3
|
|
import random
|
|
|
|
throw = random.randint(1, 6)
|
|
|
|
print(throw)
|
|
```
|
|
|
|
⛑ **Autocomplete is your friend. You can use it to browse around a library and discover interesting functions or classes you can make use of.**
|
|
|
|
A second widely used library is `datetime`.
|
|
It facilitates handling dates, hours, calendars, time differences, etc.
|
|
A simple program we can write to illustrate it's purpose is a *will-it-be-Sunday* program.
|
|
You give date and the program tells you if it's a Sunday or not.
|
|
|
|
```python3
|
|
import datetime
|
|
|
|
sunday = 6
|
|
|
|
date = input("which date should I look up? (example: 2022 3 23) ")
|
|
year, month, day = date.split()
|
|
|
|
date = datetime.date(int(year), int(month), int(day))
|
|
|
|
if date.weekday() == sunday:
|
|
print("yes! you can sleep in...")
|
|
else:
|
|
print("better set your alarm")
|
|
```
|
|
|
|
The program above incorporates a lot of different concepts.
|
|
Read it very slowly and think about what each step is doing.
|
|
Also think about how you can *break* this program!
|
|
|
|
⛑ **Why on earth is Sunday six? Read the [doc](https://docs.python.org/3/library/datetime.html#datetime.date.weekday)!**
|
|
|
|
# Coding challenge - Memento Mori calculator
|
|
|
|
[Memento Mori](https://en.wikipedia.org/wiki/Memento_mori) is a bit of a grim concept and if it freaks you out, maybe adjust the exercise to calculate the days until your next birthday.
|
|
That being said, we all die and we all live in a country with a life expectancy.
|
|
The [Belgian](https://www.healthybelgium.be/en/health-status/life-expectancy-and-quality-of-life/life-expectancy) life expectancy for a man is about 80 years so if you know your birthday, which you should, you can calculate your theoretical death day.
|
|
Plus, you can calculate the percentage of your life that remains.
|
|
You should do this in a **new** python file.
|
|
I suggest you call it `memento_mori.py` or something that makes sense to you.
|
|
|
|
<details>
|
|
<summary>Spoiler warning</summary>
|
|
|
|
```python3
|
|
import datetime
|
|
|
|
life_expectancy_years = 80.8
|
|
life_expectancy_days = life_expectancy_years * 365
|
|
delta = datetime.timedelta(days=life_expectancy_days)
|
|
|
|
date = input("what is your birthday? (example: 1986 10 7) ")
|
|
year, month, day = date.split()
|
|
|
|
birthday = datetime.date(int(year), int(month), int(day))
|
|
memento_mori = birthday + delta
|
|
|
|
days_to_live = memento_mori - datetime.datetime.now().date()
|
|
percentage = (days_to_live.days / life_expectancy_days) * 100
|
|
|
|
print("your theoretical death day is:", memento_mori)
|
|
print("that's", days_to_live.days, "days left to live")
|
|
print("which means you have {:.2f}% left to live...".format(percentage))
|
|
```
|
|
|
|
</details>
|
|
|
|
There are quite a few quirky tricks in the code above.
|
|
Take your time to ask me about them, or use online documentation and keep some notes.
|
|
|
|
# Writing your first library
|
|
|
|
Up until now have coded our own functions and usage of these functions.
|
|
We've seen there is no need to always write everything from scratch as we can *reuse* existing code by importing libraries and calling it's functions.
|
|
But what are those libraries actually?
|
|
|
|
Consider the following mini script.
|
|
It imports `datetime` and calls the `now` function to print the date and time.
|
|
A pretty basic clock.
|
|
|
|
```python3
|
|
import datetime
|
|
|
|
|
|
timestamp = datetime.datetime.now()
|
|
print("It is: {}".format(timestamp))
|
|
```
|
|
|
|
When you put your cursor on the `now` part of the function call, you can press **CTRL-b** to go to the **declaration** of said function.
|
|
You can also right click on the `now` part and see all possible *actions* you can perform on this function.
|
|
This opens up a **second tab** in your editor window with a `datetime.py` file in it.
|
|
In this file your cursor should have jumped to the following code.
|
|
|
|
```python3
|
|
@classmethod
|
|
def now(cls, tz=None):
|
|
"Construct a datetime from time.time() and optional time zone info."
|
|
t = _time.time()
|
|
return cls.fromtimestamp(t, tz)
|
|
```
|
|
|
|
What does this look like?
|
|
It looks like python code no?
|
|
Well, it is!
|
|
Libraries are often *just* other python files we load into our program.
|
|
So if they are just python files we *should* be able to write our own libraries no?
|
|
|
|
🏃 Try it
|
|
---
|
|
|
|
Import some libraries and go peak at some function declarations.
|
|
A big part of code writing is *navigating* a codebase written by other people.
|
|
Properly understanding how to navigate is essential!
|
|
|
|
## How do we write libraries?
|
|
|
|
Let's go back to our `pretty_print` function.
|
|
I have code along these lines.
|
|
|
|
```python3
|
|
def pretty_print(msg, decorator="#"):
|
|
line_len = len(msg) + (len(decorator) * 2) + 2
|
|
print(decorator * line_len)
|
|
print("{} {} {}".format(decorator, msg, decorator))
|
|
print(decorator * line_len)
|
|
|
|
pretty_print("Wouter")
|
|
pretty_print("Python rules!")
|
|
pretty_print("Alice", "-")
|
|
```
|
|
|
|
I'll **create a new python file** and name it `helper_functions.py`.
|
|
In this file I rewrite (don't copy paste, you need the practice) my function.
|
|
As I'm rewriting my function I'll also need some test calls to my function.
|
|
Done!
|
|
|
|
Now in a **second new python file** I'll name `my_program.py` I'll `import` the `helper_fucntions.py` file.
|
|
Note the syntax drops the `.py` extension!
|
|
|
|
```python3
|
|
import helper_functions
|
|
|
|
|
|
print("I'm a program")
|
|
helper_functions.pretty_print("hello world!")
|
|
```
|
|
|
|
If we now **run** the `my_program.py` file we get the following output.
|
|
|
|
```
|
|
##########
|
|
# Wouter #
|
|
##########
|
|
#################
|
|
# Python rules! #
|
|
#################
|
|
---------
|
|
- Alice -
|
|
---------
|
|
I'm a program
|
|
################
|
|
# hello world! #
|
|
################
|
|
```
|
|
|
|
OK, it *kind* of works, the function get's called from within the `my_program.py` file but the test calls from the `helper_functions.py` file are messing up my execution.
|
|
Luckily there is a way to tell python to only run certain code when a file should be seen as program and not a library.
|
|
Sounds complicated but it's logically very simple.
|
|
|
|
## What is `__name__ == "__main__"`?
|
|
|
|
Remember on the first day when I showed you how python is self documenting via `print.__doc__`?
|
|
Well, there are [more](https://stackoverflow.com/questions/20340815/built-in-magic-variable-names-attributes) *magical attributes* than just the `__doc__` one!
|
|
There is one called `__name__` which is used for the exact purpose we're trying to achieve.
|
|
Go back to the `helper_functions.py` file and comment out the test calls and add the line that print's the `__name__` variable.
|
|
|
|
```python3
|
|
def pretty_print(msg, decorator="#"):
|
|
line_len = len(msg) + (len(decorator) * 2) + 2
|
|
print(decorator * line_len)
|
|
print("{} {} {}".format(decorator, msg, decorator))
|
|
print(decorator * line_len)
|
|
|
|
|
|
print("I'm a library")
|
|
print("my name is: {}".format(__name__))
|
|
# pretty_print("Wouter")
|
|
# pretty_print("Python rules!")
|
|
# pretty_print("Alice", "-")
|
|
```
|
|
|
|
Do the same in the `my_program.py` file so it look similar to the code below.
|
|
|
|
```python3
|
|
import helper_functions
|
|
|
|
|
|
print("I'm a program")
|
|
print("my name is: {}".format(__name__))
|
|
helper_functions.pretty_print("hello world!")
|
|
```
|
|
|
|
If you now run the `helper_functions.py` file you should get output similar to this.
|
|
|
|
```
|
|
I'm a library
|
|
my name is: __main__
|
|
```
|
|
|
|
But when you run the `my_program.py` you should get something like this.
|
|
|
|
```
|
|
I'm a library
|
|
my name is: helper_functions
|
|
I'm a program
|
|
my name is: __main__
|
|
################
|
|
# hello world! #
|
|
################
|
|
```
|
|
|
|
This shows that the `__name__` variable changes according to how a script is called and this behavior can be used to our advantage!
|
|
We can *evaluate* the value behind `__name__` and change execution accordingly.
|
|
Evaluating and changing execution screams **conditional logic** no?
|
|
So in the library we add the following.
|
|
|
|
```python3
|
|
def pretty_print(msg, decorator="#"):
|
|
line_len = len(msg) + (len(decorator) * 2) + 2
|
|
print(decorator * line_len)
|
|
print("{} {} {}".format(decorator, msg, decorator))
|
|
print(decorator * line_len)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pretty_print("Wouter")
|
|
pretty_print("Python rules!")
|
|
pretty_print("Alice", "-")
|
|
```
|
|
|
|
Only when the library is executed as a program, for example when we're testing out out functions, the condition is met to allow execution of our calls.
|
|
Problem sorted!
|
|
Executing our main program now gives the expected output.
|
|
|
|
```
|
|
I'm a program
|
|
my name is: __main__
|
|
################
|
|
# hello world! #
|
|
################
|
|
```
|
|
|
|
## Anatomy of a program
|
|
|
|
While it's still very early in your coding career I *really* want to insist on good practices from the start as it will help you make sense of it all.
|
|
A well written script or program is divided into **three** sections.
|
|
|
|
1. We collect external tools from libraries, either from the standard library or of our own making.
|
|
2. We write the specific tools we need for our program to run.
|
|
3. We call a combination of **external** and **specific** tools which constitutes our program logic.
|
|
|
|
A mock-up program that follows these rules could look like this.
|
|
Notice how it's easy to read?
|
|
|
|
```python3
|
|
import random
|
|
import datetime
|
|
|
|
|
|
def first_function():
|
|
pass
|
|
|
|
|
|
def second_function():
|
|
pass
|
|
|
|
|
|
def third_function():
|
|
pass
|
|
|
|
|
|
def fourth_function():
|
|
pass
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("today it's: {}".format(datetime.datetime.now()))
|
|
choice = random.randint(0, 100)
|
|
if choice < 50:
|
|
first_function()
|
|
elif choice > 90:
|
|
second_function()
|
|
elif choice == 55:
|
|
third_function()
|
|
else:
|
|
fourth_function()
|
|
```
|
|
|
|
🏃 Try it
|
|
---
|
|
|
|
Go online and look for some scripts and programs that interest you.
|
|
See if you can assess whether the code follows the pattern I outlined.
|
|
There are a couple of things you should definitely read up on.
|
|
|
|
* The [zen](https://www.python.org/dev/peps/pep-0020/) of python.
|
|
* What is [pythonic](https://stackoverflow.com/questions/25011078/what-does-pythonic-mean)?
|
|
* Coding [style](https://docs.python-guide.org/writing/style/)
|
|
|
|
# While loop
|
|
|
|
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!
|
|
|
|
# Lists
|
|
|
|
## Creating lists
|
|
|
|
## Picking elements
|
|
|
|
## Slicing lists
|
|
|
|
# For loop
|
|
|
|
TODO say hello to my friends exercise
|
|
TODO simple ROT13 cryptography with multiple libs
|
|
|
|
# Coding challenge - Cheerleader chant
|
|
|
|
TODO nested for loop exercise
|
|
|
|
# Handling files
|
|
|
|
## Reading from a file
|
|
|
|
## Writing to a file
|
|
|
|
## csv, JSON and yaml
|
|
|
|
## pickle
|
|
|
|
# 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
|
|
|
|
# Dictionaries as data containers
|
|
|
|
TODO adapt the login generator to output a dict
|
|
|
|
# Creating our own classes
|
|
|
|
## Class examples
|
|
|
|
TODO simple animal or vehicle exercise
|
|
TODO task manager
|
|
|
|
## Class inheritance
|
|
|
|
TODO shapes and surfaces
|
|
TODO superhero game
|
|
|
|
## Improve the login generator
|
|
|
|
TODO convert the login generator 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
|
|
|
|
## Trivial pursuit multiple choice game
|
|
|
|
TODO [db](https://opentdb.com/api_config.php)
|
|
|
|
### Introduction to the `requests` library
|
|
|
|
## Threading
|
|
|
|
TODO add a countdown timer to the multiple choice game
|
|
|
|
* how apt does it's [progress bar](https://mdk.fr/blog/how-apt-does-its-fancy-progress-bar.html)
|
|
|
|
# GUI programming
|
|
|
|
## wxpython helloworld
|
|
|
|
## wxpython guess the number
|
|
|
|
## MVC design pattern
|
|
|
|
# Coding challenge - Login generator with GUI
|
|
|
|
# Coding challenge - Trivial pursuit with GUI
|
|
|
|
# Introduction to the `logging` library
|
|
|