|
|
|
|
@@ -0,0 +1,441 @@
|
|
|
|
|
# Day 4
|
|
|
|
|
|
|
|
|
|
## "While"
|
|
|
|
|
|
|
|
|
|
The last section covered the for loop - a useful loop when you have a finite number of things you need to do something with. A grocery list, api data, and application arguments could be massive but will always have a definite size. Let's talk about times when you don't know how many times to loop.
|
|
|
|
|
|
|
|
|
|
### Example 1: A Game
|
|
|
|
|
|
|
|
|
|
Let's imagine you're coding a checkers app. You might approach it like this:
|
|
|
|
|
|
|
|
|
|
1. Game Start
|
|
|
|
|
2. Player 1 moves
|
|
|
|
|
3. Check if player 2 has no remaining pieces
|
|
|
|
|
4. Player 2 moves
|
|
|
|
|
5. Check if player 1 has no remaining pieces
|
|
|
|
|
6. Player 1 moves
|
|
|
|
|
7. Check if player 2 has no remaining pieces
|
|
|
|
|
8. Player 2 moves
|
|
|
|
|
9. Check if player 1 has no remaining pieces
|
|
|
|
|
10. ""
|
|
|
|
|
11. ""
|
|
|
|
|
12. ""
|
|
|
|
|
13. ...
|
|
|
|
|
|
|
|
|
|
You'll need to do a lot of the same stuff over and over - that's great for a loop. Since you'll have no idea how long the game will go on you can't use a for loop because there aren't a finite number of things to loop through (although you could simple use [the longest checkers game](http://www.wylliedraughts.com/LongGame.htm#:~:text=The%20longest%20game%20that%20Wyllie,of%203%2Dmove%20restriction%20checkers.) as your starting point I suppose).
|
|
|
|
|
|
|
|
|
|
What we need is a loop that run indefinitey until a condition is met - namely that one player wins the game.
|
|
|
|
|
|
|
|
|
|
### Example 2: User Input
|
|
|
|
|
|
|
|
|
|
Let's build a menu system for a text-based app.
|
|
|
|
|
|
|
|
|
|
1. Open your python interpretor by typing `python` and pressing ++enter++
|
|
|
|
|
2. In your python interpretor paste the following:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
program_choices = ["print", "count", "add", "exit"]
|
|
|
|
|
while True:
|
|
|
|
|
choice = input(f"Please choose one of the following options: [{', '.join(program_choices)}]: ")
|
|
|
|
|
if choice == "print":
|
|
|
|
|
print("hello")
|
|
|
|
|
elif choice == "count":
|
|
|
|
|
print(", ".join(["1","2","3","4","5"]))
|
|
|
|
|
elif choice == "add":
|
|
|
|
|
num1 = input("first number: ")
|
|
|
|
|
num2 = input("second number: ")
|
|
|
|
|
try:
|
|
|
|
|
print(f"Answer: {int(num1) + int(num2)}")
|
|
|
|
|
except ValueError:
|
|
|
|
|
print("You did not provide 2 valid numbers")
|
|
|
|
|
elif choice == "exit":
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
print("invalid choice!")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
3. Exit the menu by typing 'exit'
|
|
|
|
|
4. Type exit() to exit the python interpretor
|
|
|
|
|
|
|
|
|
|
This lets a user select from a variety of options and returns them to the start of the menu upon a selection. If their selection is invalid we tell them it's invalid and return them to the start anyway.
|
|
|
|
|
|
|
|
|
|
Since we don't know how many things a user will want to do with our menu we should use a while loop to loop indefinitely.
|
|
|
|
|
|
|
|
|
|
Note the line that says `break`. This is how you break out of a loop in python. as soon as you call `break` it will stop the loop.
|
|
|
|
|
|
|
|
|
|
### Easy looping
|
|
|
|
|
|
|
|
|
|
Now that we've seen a few examples let's run through the core of a while loop:
|
|
|
|
|
|
|
|
|
|
1. Type `python` and press ++enter++ to open the python interpretor.
|
|
|
|
|
2. We can use a while loop to do something indefinitely. Type the following and press ++enter++ twice:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
while True:
|
|
|
|
|
print("hello")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
3. Press ++ctrl+c++ to stop the loop
|
|
|
|
|
4. We can use a while loop like a for loop (though this isn't recommended - it's too easy to get stuck in an infinite loop). Type the following and press ++enter++ twice:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
count_to = 10
|
|
|
|
|
start_at = 1
|
|
|
|
|
while start_at <= count_to:
|
|
|
|
|
print(start_at)
|
|
|
|
|
start_at += 1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
5. We can use a while loop to check multiple conditions. Type the following and press ++enter++:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import random
|
|
|
|
|
day = 1
|
|
|
|
|
raining = True
|
|
|
|
|
temperature = "cold"
|
|
|
|
|
while temperature == "cold" or raining == True:
|
|
|
|
|
print(f"Day {day}: Stay inside")
|
|
|
|
|
day += 1
|
|
|
|
|
temperature = random.choice(["cold", "warm"])
|
|
|
|
|
raining = random.choice([True, False])
|
|
|
|
|
|
|
|
|
|
print(f"Day {day}: It's safe")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
6. We can put while loops inside while loops (notice how we break out of each loop - this requires 2 break statements). Type the following and press ++enter++:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
while True:
|
|
|
|
|
print("loop 1")
|
|
|
|
|
while True:
|
|
|
|
|
print("loop 2")
|
|
|
|
|
break
|
|
|
|
|
break
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Let's build a menu
|
|
|
|
|
|
|
|
|
|
We're going to build a piece of software that lets us interact with our people API. We should be able to:
|
|
|
|
|
|
|
|
|
|
1. List the people from our API
|
|
|
|
|
2. Update the list by calling the API
|
|
|
|
|
3. Clear the list
|
|
|
|
|
4. Exit
|
|
|
|
|
|
|
|
|
|
Let's get started:
|
|
|
|
|
|
|
|
|
|
1. Create a new file in your root directory called "menu.py"
|
|
|
|
|
2. Add the following to the top:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import requests
|
|
|
|
|
import json
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
3. Create the list_people function by adding the following:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="5-7"
|
|
|
|
|
import requests
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
def list_people(people):
|
|
|
|
|
print(people)
|
|
|
|
|
return people
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
4. Now create the update_people function by adding the following:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="5-12"
|
|
|
|
|
def list_people(people):
|
|
|
|
|
print(people)
|
|
|
|
|
return people
|
|
|
|
|
|
|
|
|
|
def update_people(people):
|
|
|
|
|
try:
|
|
|
|
|
response = requests.get("http://localhost:8000/people")
|
|
|
|
|
people = response.json()["data"]
|
|
|
|
|
print("successfully updated people.")
|
|
|
|
|
except requests.exceptions.ConnectionError:
|
|
|
|
|
print("Server is not running. Failed to update.")
|
|
|
|
|
return people
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
5. Now create the clear_people function by adding the following:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="10-12"
|
|
|
|
|
def update_people(people):
|
|
|
|
|
try:
|
|
|
|
|
response = requests.get("http://localhost:8000/people")
|
|
|
|
|
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):
|
|
|
|
|
people = None
|
|
|
|
|
return people
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
6. Finally add the main loop:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="5-19"
|
|
|
|
|
def clear_people(people):
|
|
|
|
|
people = None
|
|
|
|
|
return people
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
people = None
|
|
|
|
|
choices = ["list", "update", "clear", "exit"]
|
|
|
|
|
while True:
|
|
|
|
|
choice = input(f"Please choose an option [{', '.join(choices)}]: ")
|
|
|
|
|
if choice == "list":
|
|
|
|
|
people = list_people(people)
|
|
|
|
|
elif choice == "update":
|
|
|
|
|
people = update_people(people)
|
|
|
|
|
elif choice == "clear":
|
|
|
|
|
people = clear_people(people)
|
|
|
|
|
elif choice == "exit":
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
print("Invalid choice. Please try again.")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
7. Check your work. Your whole program should be 35 lines long and look like this:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import requests
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
def list_people(people):
|
|
|
|
|
print(people)
|
|
|
|
|
return people
|
|
|
|
|
|
|
|
|
|
def update_people(people):
|
|
|
|
|
try:
|
|
|
|
|
response = requests.get("http://localhost:8000/people")
|
|
|
|
|
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):
|
|
|
|
|
people = None
|
|
|
|
|
return people
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
people = None
|
|
|
|
|
choices = ["list", "update", "clear", "exit"]
|
|
|
|
|
while True:
|
|
|
|
|
choice = input(f"Please choose an option [{', '.join(choices)}]: ")
|
|
|
|
|
if choice == "list":
|
|
|
|
|
people = list_people(people)
|
|
|
|
|
elif choice == "update":
|
|
|
|
|
people = update_people(people)
|
|
|
|
|
elif choice == "clear":
|
|
|
|
|
people = clear_people(people)
|
|
|
|
|
elif choice == "exit":
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
print("Invalid choice. Please try again.")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
8. Run the program by typing `python menu.py` and pressing ++enter++.
|
|
|
|
|
9. Type "list" and press ++enter++. You should see "None" printed to the screen. We haven't updated our people yet!
|
|
|
|
|
10. Type "update" and press ++enter++. You should see "Server is not running. Failed to update.". We didn't run our server!
|
|
|
|
|
11. Open a new terminal by clicking the plus icon.
|
|
|
|
|
12. Type `python manage.py runserver` and press ++enter++.
|
|
|
|
|
13. Flip back over to your other terminal by using the dropdown menu.
|
|
|
|
|
14. Type "update" and press ++enter++. You should see "successfully updated people."
|
|
|
|
|
15. Type "list" again and press ++enter++. You should see your people print out.
|
|
|
|
|
16. Type "clear" and press ++enter++.
|
|
|
|
|
17. Type "list" again and press ++enter++. Your people should be cleared.
|
|
|
|
|
18. Type "exit" to exit.
|
|
|
|
|
19. Flip back over to your django terminal with the dropdown menu
|
|
|
|
|
20. Press ++ctrl+c++ to stop the server
|
|
|
|
|
|
|
|
|
|
## Threading
|
|
|
|
|
|
|
|
|
|
One of the more difficult concepts in programming is threading and multiprocessing. It's rarely taught at an intro level but it's fairly easy to use.
|
|
|
|
|
|
|
|
|
|
A program runs in a "thread". When you run `python menu.py` it creates one thread that executes all code in order. Code at the end of your file can't run before code at the beginning of your file.
|
|
|
|
|
|
|
|
|
|
...unless it could. What if you have a super slow internet connection and you need to make an api call? You don't want it to slow down your whole menu.
|
|
|
|
|
|
|
|
|
|
Here's the idea: we tell our computer to make the slow call to our API in the background and continue letting the user mess with the menu.
|
|
|
|
|
|
|
|
|
|
Let's make that a reality by simulating a slow internet connection.
|
|
|
|
|
|
|
|
|
|
1. Open views.py
|
|
|
|
|
2. For this part we're going to need the time library. "time" lets us pause code execution for a bit, simulating a slow internet. At the top of views.py add the following:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="2 2"
|
|
|
|
|
import random
|
|
|
|
|
import time
|
|
|
|
|
from django.shortcuts import render
|
|
|
|
|
from django.http import JsonResponse
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
3. Jump to the very bottom and add the highlighted code:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="10-23"
|
|
|
|
|
def api(request):
|
|
|
|
|
people = [
|
|
|
|
|
{"first name" : "Jim", "last name": "Fowler", "age": 24},
|
|
|
|
|
{"first name" : "Bob", "last name": "Jones", "age": 36},
|
|
|
|
|
{"first name" : "Alice", "last name": "Appleseed", "age": 52}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
return JsonResponse({"data": people})
|
|
|
|
|
|
|
|
|
|
def slow_api(request):
|
|
|
|
|
people = []
|
|
|
|
|
first_names = ["Liam", "Noah", "Oliver", "William", "Olivia", "Emma", "Ava", "Sophia", "Isabella"]
|
|
|
|
|
last_names = ["Smith", "Johnson", "Anderson", "Brown", "Garcia", "Miller", "Martinez", "Chavez"]
|
|
|
|
|
num_people = random.randint(1,100)
|
|
|
|
|
|
|
|
|
|
for person in range(num_people):
|
|
|
|
|
time.sleep(.1)
|
|
|
|
|
first_name = random.choice(first_names)
|
|
|
|
|
last_name = random.choice(last_names)
|
|
|
|
|
age = random.randint(1,100)
|
|
|
|
|
people.append({"first_name": first_name, "last_name": last_name, "age": age})
|
|
|
|
|
|
|
|
|
|
return JsonResponse({"data": people})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
4. Save with ++ctrl+s++
|
|
|
|
|
5. We need to add a URL, open urls.py
|
|
|
|
|
6. Add the highlighted code:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="8 8"
|
|
|
|
|
from django.urls import path
|
|
|
|
|
from . import views
|
|
|
|
|
|
|
|
|
|
urlpatterns = [
|
|
|
|
|
path('', views.index),
|
|
|
|
|
path('weather/', views.weather),
|
|
|
|
|
path('people/', views.api),
|
|
|
|
|
path('slow/', views.slow_api),
|
|
|
|
|
]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
7. Save with ++ctrl+s++
|
|
|
|
|
8. Open a new terminal by clicking the plus icon.
|
|
|
|
|
9. Type `python manage.py runserver` and press ++enter++.
|
|
|
|
|
10. Flip back over to your other terminal by using the dropdown menu.
|
|
|
|
|
11. Navigate to <http://localhost:8000/slow/> to see your api results. Notice how long the page takes to load.
|
|
|
|
|
12. Let's try our menu.py with the new API. Open menu.py and update line 10:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="3 3"
|
|
|
|
|
def update_people(people):
|
|
|
|
|
try:
|
|
|
|
|
response = requests.get("http://localhost:8000/slow")
|
|
|
|
|
people = response.json()["data"]
|
|
|
|
|
print("successfully updated people.")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
13. Save with ++ctrl+s++
|
|
|
|
|
14. Open a new terminal by clicking the plus icon
|
|
|
|
|
15. Type `python menu.py` and press ++enter++
|
|
|
|
|
16. Type "list" and press ++enter++. You should see `None`
|
|
|
|
|
17. Type "update" and press ++enter++. Notice the delay. While we're waiting for our api to respond our menu won't respond to any thing we type.
|
|
|
|
|
18. Type "exit" to exit.
|
|
|
|
|
19. Let's add the magic that will unblock our menu during an API call. At the top of menu.py add the following:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="3 3"
|
|
|
|
|
import requests
|
|
|
|
|
import json
|
|
|
|
|
import 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.
|
|
|
|
|
|
|
|
|
|
A future is something that hasn't finished executing yet. We don't know when it will finish but we assume that it will.
|
|
|
|
|
|
|
|
|
|
```python hl_lines="3 8 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":
|
|
|
|
|
people = list_people(future, people)
|
|
|
|
|
elif choice == "update":
|
|
|
|
|
future = ThreadPoolExecutor().submit(update_people, people)
|
|
|
|
|
elif choice == "clear":
|
|
|
|
|
people = clear_people(people)
|
|
|
|
|
elif choice == "exit":
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
print("Invalid choice. Please try again.")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
21. We need to modify the `list_people` function to handle a future. This ensures that new people will print after an update. Change the following:
|
|
|
|
|
|
|
|
|
|
```python hl_lines="1-4"
|
|
|
|
|
def list_people(future, people):
|
|
|
|
|
if future is not None and future.done():
|
|
|
|
|
people = future.result()
|
|
|
|
|
future = None
|
|
|
|
|
print(people)
|
|
|
|
|
return people
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
22. Save with ++ctrl+s++
|
|
|
|
|
23. Your menu.py should look like this:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import requests
|
|
|
|
|
import json
|
|
|
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
|
|
|
|
|
|
def list_people(future, people):
|
|
|
|
|
if future is not None and future.done():
|
|
|
|
|
people = future.result()
|
|
|
|
|
future = None
|
|
|
|
|
print(people)
|
|
|
|
|
return people
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
def clear_people(people):
|
|
|
|
|
people = None
|
|
|
|
|
return people
|
|
|
|
|
|
|
|
|
|
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":
|
|
|
|
|
people = list_people(future, people)
|
|
|
|
|
elif choice == "update":
|
|
|
|
|
future = ThreadPoolExecutor().submit(update_people, people)
|
|
|
|
|
elif choice == "clear":
|
|
|
|
|
people = clear_people(people)
|
|
|
|
|
elif choice == "exit":
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
print("Invalid choice. Please try again.")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
24. Run
|
|
|
|
|
25. Now run `python menu.py`
|
|
|
|
|
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++
|
|
|
|
|
29. Now type `update` and press ++enter++ and quickly type `list` and press ++enter++ before it updates. Notice you can interact with the menu before the result returns!
|
|
|
|
|
30. You've successfully written a multithreaded program. Type `exit` to exit.
|
|
|
|
|
31. Flip back to your django server terminal with the dropdown
|
|
|
|
|
32. Press ++ctrl+c++ to stop the server.
|