Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
948996705e | ||
|
|
e09bd887b5 | ||
|
|
7358db6429 |
20
docs/day4.md
@@ -129,14 +129,12 @@ Let's get started:
|
||||
|
||||
```python
|
||||
import requests
|
||||
import json
|
||||
```
|
||||
|
||||
3. Create the list_people function by adding the following:
|
||||
|
||||
```python hl_lines="5-7"
|
||||
```python hl_lines="3-5"
|
||||
import requests
|
||||
import json
|
||||
|
||||
def list_people(people):
|
||||
print(people)
|
||||
@@ -346,8 +344,7 @@ Let's make that a reality by simulating a slow internet connection.
|
||||
|
||||
```python hl_lines="3 3"
|
||||
import requests
|
||||
import json
|
||||
import concurrent.futures import ThreadPoolExecutor
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
```
|
||||
|
||||
20. We need to modify our main program to call the update_people function in a different thread. To do this we need to keep track of a "future" object.
|
||||
@@ -362,7 +359,7 @@ Let's make that a reality by simulating a slow internet connection.
|
||||
while True:
|
||||
choice = input(f"Please choose an option [{', '.join(choices)}]: ")
|
||||
if choice == "list":
|
||||
people = list_people(future, people)
|
||||
future, people = list_people(future, people)
|
||||
elif choice == "update":
|
||||
future = ThreadPoolExecutor().submit(update_people, people)
|
||||
elif choice == "clear":
|
||||
@@ -381,7 +378,7 @@ Let's make that a reality by simulating a slow internet connection.
|
||||
people = future.result()
|
||||
future = None
|
||||
print(people)
|
||||
return people
|
||||
return (future, people)
|
||||
```
|
||||
|
||||
22. Save with ++ctrl+s++
|
||||
@@ -389,7 +386,6 @@ Let's make that a reality by simulating a slow internet connection.
|
||||
|
||||
```python
|
||||
import requests
|
||||
import json
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
def list_people(future, people):
|
||||
@@ -397,7 +393,7 @@ Let's make that a reality by simulating a slow internet connection.
|
||||
people = future.result()
|
||||
future = None
|
||||
print(people)
|
||||
return people
|
||||
return (future, people)
|
||||
|
||||
def update_people(people):
|
||||
try:
|
||||
@@ -419,7 +415,7 @@ Let's make that a reality by simulating a slow internet connection.
|
||||
while True:
|
||||
choice = input(f"Please choose an option [{', '.join(choices)}]: ")
|
||||
if choice == "list":
|
||||
people = list_people(future, people)
|
||||
future, people = list_people(future, people)
|
||||
elif choice == "update":
|
||||
future = ThreadPoolExecutor().submit(update_people, people)
|
||||
elif choice == "clear":
|
||||
@@ -430,8 +426,8 @@ Let's make that a reality by simulating a slow internet connection.
|
||||
print("Invalid choice. Please try again.")
|
||||
```
|
||||
|
||||
24. Run
|
||||
25. Now run `python menu.py`
|
||||
24. Now run `python menu.py`
|
||||
25. Type `list` and press ++enter++. Notice how there's nothing in the list.
|
||||
26. Type `update` and press ++enter++. Notice the menu returns instantly.
|
||||
27. In a moment you'll see "successfully updated people" print. Type `list` and press ++enter++.
|
||||
28. Type `clear` and press ++enter++
|
||||
|
||||
338
docs/day5.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# Day 5
|
||||
|
||||
## "import"
|
||||
|
||||
In our last section we're going to talk about organization. Not a glamorous topic, but an important one nonetheless!
|
||||
|
||||
### What an import actually is
|
||||
|
||||
You've already done this quite a bit - but here's a recap
|
||||
|
||||
1. Type `python` in the terminal and press ++enter++ to start the interpretor
|
||||
2. Type `requests.get("https://google.com")` and press ++enter++
|
||||
3. You should see `NameError: name 'requests' is not defined` printed to the terminal
|
||||
4. Type `import requests` and press ++enter++
|
||||
5. Now type `requests.get("https://google.com")` again and press ++enter++
|
||||
6. You should see `<Response [200]>` printed to the terminal. You've just imported a module.
|
||||
7. Type `exit()` and press ++enter++ to exit
|
||||
|
||||
"importing" alerts python that you want to use some code that isn't in the file you're currently working in. But where is "requests"? Remember that "venv" folder we made? Look inside it:
|
||||
|
||||

|
||||
|
||||
Requests is just a bit of python code that lives in our venv. In fact, we can rename it and import it as something completely different (though this isn't recommended). Give this a try:
|
||||
|
||||
1. Rename the requests folder in your venv to "somethingelse"
|
||||
2. Type `python` in the terminal and press ++enter++ to open the interpretor
|
||||
3. Type `import somethingelse` and press ++enter++
|
||||
4. Type `somethingelse.get("https://google.com")` and press ++enter++. You should see `<Response [200]>` printed to the terminal. Your rename worked! You just changed the name python references when we import the code.
|
||||
5. Type `exit()` and press ++enter++ to exit.
|
||||
6. Rename "somethingelse" back to " for now.
|
||||
|
||||
|
||||
### Using "as" to rename imports
|
||||
|
||||
Sometimes we don't want to use the name of the package in our code. It might conflict with something we already named, it might be really long to type, or it might just be poorly named. Fortunately, we don't have to rename the folder to change the name.
|
||||
|
||||
1. Type `python` in the terminal and press ++enter++ to open the interpretor
|
||||
2. Type `import requests as somethingelse` and press enter
|
||||
3. Type `somethingelse.get("https://google.com")` and press ++enter++. You should see `<Response [200]>` printed to the terminal. You can use `as` to rename modules.
|
||||
4. Type `import requests` and press ++enter++
|
||||
5. Type `requests.get("https://google.com")` and press ++enter++. You should see `<Response [200]>` printed to the terminal. You can import requests like normal alongside your custom name.
|
||||
6. Type `exit()` and press ++enter++ to exit
|
||||
|
||||
### dots
|
||||
|
||||
When you type `requests.get()` have you considered what the `.` means? Anytime you see a `.` it means you can split that thing apart. Example:
|
||||
|
||||
```text
|
||||
requests . get
|
||||
└──────┘ ^ └─┘
|
||||
```
|
||||
|
||||
are two separate things.
|
||||
|
||||
`requests` is a "package". A package can container other packages, classes, functions, variables, etc.
|
||||
|
||||
`get` is a "function". That means it was created like this:
|
||||
|
||||
```python
|
||||
def get(url, ...):
|
||||
*code here*
|
||||
```
|
||||
|
||||
Check around line 64 in `venv/lib/python3/site-packages/requests/api.py` to see the actual function.
|
||||
|
||||
Let's actually make use of that dot.
|
||||
|
||||
1. Type `python` in the terminal and press ++enter++
|
||||
2. Type `from requests import get` and press ++enter++
|
||||
3. Type `requests.get("https://google.com")` and press ++enter++
|
||||
4. You should see `NameError: name 'requests' is not defined` printed to the terminal
|
||||
5. Type `get("https://google.com")` and press ++enter++. You should see `<Response [200]>` printed to the terminal. You just split the `get` function from the `requests` package. You don't need to type `requests.` to use the `get` function.
|
||||
6. Type `post("https://google.com")` and press ++enter++. You should see `NameError: name 'post' is not defined` printed to the terminal. `post` is a valid function, let's import it.
|
||||
7. Type `from requests import *` and press ++enter++. We just used a "wildcard" import. It import everything into our interpretor from the package..
|
||||
8. Type `post("https://google.com")` and press ++enter++. You should see `<Response [405]>` printed to the terminal. You didn't need to `from requests import post` to use the `post` function. You imported it when you used the `*` symbol.
|
||||
9. Type `exit()` and press ++enter++ to exit.
|
||||
|
||||
## Breaking apart our terrible weather app
|
||||
|
||||
What better way to demonstrate the power of imports than with our terrible weather app?
|
||||
|
||||
1. Type `python weather_app.py` in your terminal and press ++enter++. You should run through your terrible weather app just like on day 2
|
||||
2. Open "weather_app.py" in VSCode
|
||||
3. Notice the `import random` at the top, you should know what's happening here.
|
||||
4. Create a new file called `weather_config.py` in the root directory
|
||||
|
||||

|
||||
|
||||
5. Copy the 4 variables from the top of "weather_app.py" into "weather_config.py"
|
||||
|
||||

|
||||
|
||||
6. Save with ++ctrl+s++
|
||||
|
||||
7. Delete those variables from "weather_app.py".
|
||||
|
||||

|
||||
|
||||
8. Save with ++ctrl+s++
|
||||
|
||||
9. Delete `import random` from the top of "weather_app.py"
|
||||
10. Save with ++ctrl+s++
|
||||
11. Add `import random` to the top of "weather_config.py"
|
||||
|
||||
```python hl_lines="1 2"
|
||||
import random
|
||||
|
||||
warm = random.choice([True, False])
|
||||
cold = not warm
|
||||
raining = random.choice([True, False])
|
||||
snowing = not raining
|
||||
```
|
||||
|
||||
12. Save with ++ctrl+s++
|
||||
13. In your terminal type `python weather_app.py` and press ++enter++. You should see `NameError: name 'warm' is not defined` print to the terminal. But `warm` is in our weather_config.py! We need to import it!
|
||||
14. At the top of "weather_app.py" add `from weather_config import warm`
|
||||
|
||||
```python hl_lines="1 2"
|
||||
from weather_config import warm
|
||||
|
||||
if warm or cold:
|
||||
print("It's warm or cold.")
|
||||
|
||||
if raining or warm:
|
||||
```
|
||||
|
||||
15. Save with ++ctrl+s++
|
||||
16. Type `python weather_app.py` again and press ++enter++. This time you should see `NameError: name 'cold' is not defined` printed to your terminal. Ah, we imported warm but none of the other variables. We need to import those as well. We could type everything out one by one, but instead:
|
||||
17. At the top of "weather_app.py" change `from weather_config import warm` to `from weather_config import *`
|
||||
18. Save with ++ctrl+s++
|
||||
19. Now type `python weather_app.py` again and press ++enter++. Your weather app works as normal!
|
||||
|
||||
### Breaking it even further apart
|
||||
|
||||
We're going to turn each "chunk" of code in our weather app into a function that we can move to another file.
|
||||
|
||||
1. Starting at line 3, add the following (**indent the if statements!**):
|
||||
|
||||
```python hl_lines="1 1"
|
||||
def print_clues():
|
||||
--->if warm or cold:
|
||||
--->--->print("It's warm or cold.")
|
||||
|
||||
--->if raining or warm:
|
||||
--->--->print("It's raining or warm.")
|
||||
|
||||
--->if raining or snowing:
|
||||
--->--->print("It's raining or snowing.")
|
||||
|
||||
--->if cold or snowing:
|
||||
--->--->print("It's cold or snowing.")
|
||||
```
|
||||
|
||||

|
||||
|
||||
2. Now do the same to our "guess chunks" like so:
|
||||
|
||||
```python hl_lines="1 1"
|
||||
def check_guesses():
|
||||
--->warm_guess = input("Is it warm? (y/n) ")
|
||||
--->if warm_guess == 'y' and warm:
|
||||
--->--->print('Correct!')
|
||||
--->elif warm_guess == 'n' and not warm:
|
||||
--->--->print('Correct!')
|
||||
--->else:
|
||||
--->--->print('Wrong!')
|
||||
|
||||
--->cold_guess = input("Is it cold? (y/n) ")
|
||||
--->if cold_guess == 'y' and cold:
|
||||
--->--->print('Correct!')
|
||||
--->elif cold_guess == 'n' and not cold:
|
||||
--->--->print('Correct!')
|
||||
--->else:
|
||||
--->--->print('Wrong!')
|
||||
|
||||
--->raining_guess = input("Is it raining? (y/n) ")
|
||||
--->if raining_guess == 'y' and raining:
|
||||
--->--->print('Correct!')
|
||||
--->elif raining_guess == 'n' and not raining:
|
||||
--->--->print('Correct!')
|
||||
--->else:
|
||||
--->--->print('Wrong!')
|
||||
|
||||
--->snowing_guess = input("Is it snowing? (y/n) ")
|
||||
--->if snowing_guess == 'y' and snowing:
|
||||
--->--->print('Correct!')
|
||||
--->elif snowing_guess == 'n' and not snowing:
|
||||
--->--->print('Correct!')
|
||||
--->else:
|
||||
--->--->print('Wrong!')
|
||||
```
|
||||
|
||||

|
||||
|
||||
3. Save with ++ctrl+s++
|
||||
4. Type `python weather_app.py` in the terminal and press ++enter++. Nothing happens!
|
||||
5. Create a new file called "weather_run.py" in your root directory.
|
||||
|
||||

|
||||
|
||||
6. Add the following to "weather_run.py"
|
||||
|
||||
```python
|
||||
from weather_app import print_clues, check_guesses
|
||||
|
||||
print_clues()
|
||||
check_guesses()
|
||||
```
|
||||
|
||||
7. Save with ++ctrl+s++
|
||||
8. In your terminal type `python weather_run.py` and press ++enter++. Your weather app works again! You've broken it out into a bunch of modules.
|
||||
9. Type `python` in your terminal and press ++enter++ to open the python interpretor
|
||||
10. Type `import weather_run` and press ++enter++. Your weather app should run in the terminal
|
||||
11. Press ++ctrl+c++ to stop your app from running
|
||||
12. Type `exit()` to exit
|
||||
13. We don't want your app to run when we import it. Let's add a check to "weather_run.py" to make sure that doesn't happen. Modify "weather_run.py" like so:
|
||||
|
||||
```python hl_lines="3 3"
|
||||
from weather_app import print_clues, check_guesses
|
||||
|
||||
if __name__ == "__main__":
|
||||
--->print_clues()
|
||||
--->check_guesses()
|
||||
```
|
||||
|
||||

|
||||
|
||||
14. Type `python` in your terminal and press ++enter++ to open the python interpretor
|
||||
15. Type `import weather_run` and press ++enter++. This time nothing should happen! That's good, we want to import our app without it running immediately. Just like importing `requests` doesn't immediately run the `get` function, we don't want anything to run on import of our programs.
|
||||
16. Type `exit()` to exit
|
||||
|
||||
## Importing our menu
|
||||
|
||||
We can use imports to make our lives a lot easier. Let's use our menu as an example.
|
||||
|
||||
1. Open "menu.py" in VSCode
|
||||
2. Type `python` in your terminal and press ++enter++ to open the python interpretor
|
||||
3. Type `import menu` and press enter. You've just imported all our functions from "menu.py". Notice how the menu doesn't run though, that's because we added a `if __name__ == "__main__"` block! We were thinking ahead.
|
||||
4. Open a new terminal windows by clicking the plus icon
|
||||
5. Type `python manage.py runserver` and press ++enter++ to start your server
|
||||
6. Use the dropdown to switch back to your other terminal window
|
||||
7. Type `menu.update_people(None)` and press ++enter++. You should see people print to the terminal. But wait, that's not what update() was supposed to do - and why did we have to specify `(None)`?
|
||||
|
||||
If we want our menu functions to be useful outside our menu app we have some work to do:
|
||||
|
||||
1. First, I shouldn't have to pass a default value like `(None)` to our functions. Edit `list_people` like so:
|
||||
|
||||
```python hl_lines="1 1"
|
||||
def list_people(future=None, people=None):
|
||||
if future is not None and future.done():
|
||||
people = future.result()
|
||||
future = None
|
||||
print(people)
|
||||
return (future, people)
|
||||
```
|
||||
|
||||
This specifies that unless a "future" or "people" is provided they are "None" by default.
|
||||
|
||||
2. Let's do that to our remaining functions:
|
||||
|
||||
```python hl_lines="1 10"
|
||||
def update_people(people=None):
|
||||
try:
|
||||
response = requests.get("http://localhost:8000/slow")
|
||||
people = response.json()["data"]
|
||||
print("successfully updated people.")
|
||||
except requests.exceptions.ConnectionError:
|
||||
print("Server is not running. Failed to update.")
|
||||
return people
|
||||
|
||||
def clear_people(people=None):
|
||||
people = None
|
||||
return people
|
||||
```
|
||||
|
||||
3. Now let's fix our update function. `update_people` is supposed to run in the background, but it just returned the list of people. Let's create a "decorator".
|
||||
|
||||
4. At the top of `menu.py` add the following:
|
||||
|
||||
```python hl_lines="5-8"
|
||||
import requests
|
||||
import json
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
def run_in_background(function):
|
||||
def wrapper(*args, **kwargs):
|
||||
return ThreadPoolExecutor().submit(function, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
def list_people(future, people):
|
||||
if future is not None and future.done():
|
||||
people = future.result()
|
||||
future = None
|
||||
```
|
||||
|
||||
5. Now add the following above `update_people()`:
|
||||
|
||||
```python hl_lines="1 1"
|
||||
@run_in_background
|
||||
def update_people(people):
|
||||
try:
|
||||
response = requests.get("http://localhost:8000/slow")
|
||||
people = response.json()["data"]
|
||||
print("successfully updated people.")
|
||||
except requests.exceptions.ConnectionError:
|
||||
print("Server is not running. Failed to update.")
|
||||
return people
|
||||
```
|
||||
|
||||
6. Remove the `ThreadPoolExecutor()` part at line 40:
|
||||
|
||||
```python hl_lines="10 10"
|
||||
if __name__ == "__main__":
|
||||
people = None
|
||||
future = None
|
||||
choices = ["list", "update", "clear", "exit"]
|
||||
while True:
|
||||
choice = input(f"Please choose an option [{', '.join(choices)}]: ")
|
||||
if choice == "list":
|
||||
future, people = list_people(future, people)
|
||||
elif choice == "update":
|
||||
future = update_people(people)
|
||||
elif choice == "clear":
|
||||
people = clear_people(people)
|
||||
elif choice == "exit":
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please try again.")
|
||||
```
|
||||
|
||||
7. Let's make sure we didn't break our menu. Type `python menu.py` and press ++enter++
|
||||
8. Type update, list, clear, and exit to test your program.
|
||||
9. Type `python` and press ++enter++ to open the interpretor
|
||||
10. Type `import menu` and press ++enter++
|
||||
11. Type `people = menu.list_people()` and press ++enter++. You should see `None` print.
|
||||
12. Now type `update = menu.update_people()` and press ++enter++. Nothing should print.
|
||||
13. Wait a moment for "successfully updated people." to print to the terminal
|
||||
14. Type `people = menu.list_people(update)`. Your people should list! You successfully turned your menu into an importable package!
|
||||
BIN
docs/img/day5/clues.png
Normal file
|
After Width: | Height: | Size: 668 KiB |
BIN
docs/img/day5/config.gif
Normal file
|
After Width: | Height: | Size: 990 KiB |
BIN
docs/img/day5/copy.gif
Normal file
|
After Width: | Height: | Size: 933 KiB |
BIN
docs/img/day5/delete.gif
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
docs/img/day5/guesses.png
Normal file
|
After Width: | Height: | Size: 692 KiB |
BIN
docs/img/day5/main.png
Normal file
|
After Width: | Height: | Size: 578 KiB |
BIN
docs/img/day5/requests.gif
Normal file
|
After Width: | Height: | Size: 3.2 MiB |
BIN
docs/img/day5/run.gif
Normal file
|
After Width: | Height: | Size: 768 KiB |
@@ -33,4 +33,9 @@
|
||||
|
||||
- "While"
|
||||
- Let's build a menu
|
||||
- Threading
|
||||
- Threading
|
||||
|
||||
### [Day 5](day5.md): import
|
||||
|
||||
- "import"
|
||||
-
|
||||