mirror of
https://github.com/marcel-dempers/docker-development-youtube-series.git
synced 2025-06-06 17:01:30 +00:00
commit
029c91e3e0
434
python/introduction/README.md
Normal file
434
python/introduction/README.md
Normal file
@ -0,0 +1,434 @@
|
||||
# Introduction to Learning Python
|
||||
|
||||
Best place to start is the documentation [www.python.org](https:#www.python.org/) <br/>
|
||||
|
||||
Download [Python 3](https:#www.python.org/downloads/) <br/>
|
||||
|
||||
Docker images for Python on [Docker Hub](https:#hub.docker.com/_/python) <br/>
|
||||
|
||||
|
||||
# Run Python in Docker
|
||||
|
||||
We can also run python in a docker container: <br/>
|
||||
|
||||
```
|
||||
cd python\introduction
|
||||
|
||||
docker build --target dev . -t python
|
||||
docker run -it -v ${PWD}:/work python sh
|
||||
|
||||
/work # python --version
|
||||
Python 3.9.
|
||||
|
||||
```
|
||||
|
||||
# Our first Program
|
||||
|
||||
* Create a folder containing our application source code
|
||||
|
||||
```
|
||||
mkdir src
|
||||
|
||||
```
|
||||
|
||||
* Create basic source code
|
||||
|
||||
In the `src` folder, create a program called `app.py`
|
||||
Paste the following content into it.
|
||||
|
||||
```
|
||||
|
||||
def main():
|
||||
print("Hello, world.")
|
||||
|
||||
main()
|
||||
```
|
||||
|
||||
* Run your application code
|
||||
|
||||
You can run your application
|
||||
|
||||
```
|
||||
/work # python src/app.py
|
||||
Hello, world.
|
||||
```
|
||||
|
||||
# The Code
|
||||
|
||||
In the video we cover writing functions. </br>
|
||||
It allows us to execute a block of code <br/>
|
||||
You want to give your function a single purpose <br/>
|
||||
Functions can have an input and return an output <br/>
|
||||
Well thought out functions makes it easier to write tests <br/>
|
||||
|
||||
Instead of doing a boring `x + y` function that adds two numbers, let's do something a little
|
||||
more realistic but still basic:
|
||||
|
||||
```
|
||||
# This function returns some data
|
||||
# The data could be coming from a database, or a file.
|
||||
# The person calling the function should not care
|
||||
# how the data is retrieved.
|
||||
# Since the function does not leak its Data provider
|
||||
|
||||
def getCustomer():
|
||||
return "Marcel Dempers"
|
||||
|
||||
print(getCustomer())
|
||||
|
||||
```
|
||||
|
||||
# Function inputs
|
||||
|
||||
As we saw, functions can return outputs, and also take inputs.
|
||||
Let's accept a customer ID and return a customer record with that ID:
|
||||
|
||||
```
|
||||
def getCustomer(customerID):
|
||||
return "CustomerID: " + customerID + ", Name: Marcel Dempers"
|
||||
|
||||
print(getCustomer("abc"))
|
||||
```
|
||||
|
||||
|
||||
## Variables
|
||||
|
||||
To hold data in programming languages, we use variables. <br/>
|
||||
Variables take up space in memory, so we want to keep it minimal. <br/>
|
||||
Let's declare variables in our function:
|
||||
|
||||
```
|
||||
def getCustomer(customerID):
|
||||
firstName = "Marcel"
|
||||
lastName = "Dempers"
|
||||
fullName = firstName + " " + lastName
|
||||
|
||||
return fullName
|
||||
|
||||
#or we can return the computation instead of adding another variable!
|
||||
return firstName + " " + lastName
|
||||
|
||||
#or we don't even need to declare variables :)
|
||||
return "Marcel Dempers"
|
||||
|
||||
customer = getCustomer("abc")
|
||||
print(customer)
|
||||
|
||||
```
|
||||
|
||||
## Control Flows (if\else)
|
||||
|
||||
You can see we're not using the `customerId` input in our function. <br/>
|
||||
Let's use it and showcase control flows to create logic! <br/>
|
||||
|
||||
Control flows allow us to add "rules" to our code. </br>
|
||||
"If this is the case, then do that, else do something else".
|
||||
|
||||
So let's say we have a customer ID 1 coming in, we may only want to
|
||||
return our customer if it matches the `customerID`
|
||||
|
||||
```
|
||||
|
||||
def getCustomer(customerID):
|
||||
if customerID == "abc":
|
||||
return "Marcel Dempers"
|
||||
elif customerID == "def":
|
||||
return "Bob Smith"
|
||||
else:
|
||||
return ""
|
||||
|
||||
customer = getCustomer("abc")
|
||||
print(customer)
|
||||
|
||||
```
|
||||
|
||||
Let's invoke our function :
|
||||
|
||||
```
|
||||
marcel = getCustomer("abc")
|
||||
print(marcel)
|
||||
|
||||
bob = getCustomer("def")
|
||||
print(bob)
|
||||
|
||||
```
|
||||
|
||||
## Arrays
|
||||
|
||||
Our function can only return one customer at a time. <br/>
|
||||
What if we wanted to return more than one customer. <br/>
|
||||
In real world systems, we may return database records. <br/>
|
||||
This is where we start looking at arrays, lists, dictionaries etc. <br/>
|
||||
|
||||
Let's add another function to get an array of customers!
|
||||
|
||||
```
|
||||
def getCustomers():
|
||||
customers = [ "Marcel Dempers", "Bob Smith" ]
|
||||
return customers
|
||||
|
||||
customers = getCustomers()
|
||||
print(customers)
|
||||
```
|
||||
|
||||
You can get array values by index starting at `0`:
|
||||
|
||||
```
|
||||
customers = getCustomers()
|
||||
|
||||
marcel = customers[0]
|
||||
bob = customers[1]
|
||||
|
||||
print(marcel + "\n" + bob)
|
||||
|
||||
```
|
||||
|
||||
We can set a value in our Array. <br/>
|
||||
Arrays are fixed in size, notice we cannot just access a
|
||||
record : <br/>
|
||||
|
||||
```
|
||||
customers[2] = "John Smith"
|
||||
IndexError: list assignment index out of range
|
||||
|
||||
```
|
||||
|
||||
Instead we can use the `append()` function of the array to add items.
|
||||
|
||||
```
|
||||
def getCustomers():
|
||||
customers = [ "Marcel Dempers", "Bob Smith" ]
|
||||
customers.append("James Baker")
|
||||
customers.append("Jonathan D")
|
||||
customers.append("Aleem Janmohamed")
|
||||
customers.append("Ivo Galic")
|
||||
customers.append("Joel Griffiths")
|
||||
customers.append("Michael Spinks")
|
||||
customers.append("Victor Savkov")
|
||||
return customers
|
||||
|
||||
```
|
||||
|
||||
We can remove items with the `remove()` function.
|
||||
|
||||
```
|
||||
customers.remove("Bob Smith")
|
||||
```
|
||||
|
||||
# Dictionaries
|
||||
|
||||
Dictionaries have an advantage whereby items can be found using a key. <br/>
|
||||
With arrays, we have to know the index number where the item is located or else we cannot find items unless we write a loop. <br/>
|
||||
With Dictionaries, we store key value pairs. So the key, may be our customer ID.<br/>
|
||||
|
||||
Let's replace our customer array with a dictionary
|
||||
```
|
||||
customers = {
|
||||
"a": "James Baker",
|
||||
"b": "Jonathan D",
|
||||
"c": "Aleem Janmohamed",
|
||||
"d": "Ivo Galic",
|
||||
"e": "Joel Griffiths",
|
||||
"f": "Michael Spinks",
|
||||
"g": "Victor Savkov"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We can also set and access an item by key
|
||||
|
||||
```
|
||||
#update or create item
|
||||
customers["h"] = "Marcel Dempers"
|
||||
|
||||
#get an item
|
||||
marcel = customers["h"]
|
||||
print(marcel)
|
||||
|
||||
```
|
||||
|
||||
Let's update our `getCustomer()` function to get customer from our dictionary
|
||||
|
||||
```
|
||||
def getCustomer(customerID):
|
||||
customer = getCustomers()
|
||||
return customer[customerID]
|
||||
|
||||
def getCustomers():
|
||||
customers = {
|
||||
"a": "James Baker",
|
||||
"b": "Jonathan D",
|
||||
"c": "Aleem Janmohamed",
|
||||
"d": "Ivo Galic",
|
||||
"e": "Joel Griffiths",
|
||||
"f": "Michael Spinks",
|
||||
"g": "Victor Savkov",
|
||||
"h" : "Marcel Dempers"
|
||||
}
|
||||
|
||||
return customers
|
||||
|
||||
customers = getCustomers()
|
||||
customer = getCustomer("h")
|
||||
|
||||
print(customer)
|
||||
print(customers)
|
||||
```
|
||||
|
||||
## Loops
|
||||
|
||||
Loops are used to iterate over collections, lists, arrays etc. <br/>
|
||||
There are two primitive loops in Python </br>
|
||||
Let's say we need to loop through our customers
|
||||
|
||||
### While loop
|
||||
|
||||
While loops are simple to write by condition:
|
||||
|
||||
```
|
||||
i = 0
|
||||
while i < 7:
|
||||
print(customers[i])
|
||||
i += 1
|
||||
```
|
||||
|
||||
We can also use this condition to loop arrays:
|
||||
|
||||
```
|
||||
customers = [ "Marcel Dempers", "Bob Smith" ]
|
||||
customers.append("James Baker")
|
||||
customers.append("Jonathan D")
|
||||
customers.append("Aleem Janmohamed")
|
||||
customers.append("Ivo Galic")
|
||||
customers.append("Joel Griffiths")
|
||||
customers.append("Michael Spinks")
|
||||
customers.append("Victor Savkov")
|
||||
i = 0
|
||||
while i <= 8:
|
||||
print(customers[i])
|
||||
i += 1
|
||||
```
|
||||
|
||||
### For loops
|
||||
|
||||
For looping a dictionary of list, `for` loops are a little easier:
|
||||
|
||||
```
|
||||
customers = getCustomers()
|
||||
|
||||
for customerID in customers:
|
||||
print(customers[customerID])
|
||||
```
|
||||
|
||||
## Classes and Objects
|
||||
|
||||
So far so good, however, customer data is not useful as strings. <br/>
|
||||
Customers can have a firstname, lastname, and more properties. <br/>
|
||||
|
||||
For this purpose we'd like to group some variables into a single variable. <br/>
|
||||
This is what `classes` allows us to do. <br/>
|
||||
Let's create a `class` for our customer
|
||||
|
||||
```
|
||||
class Customer:
|
||||
customerID = ""
|
||||
firstName = ""
|
||||
lastName = ""
|
||||
def fullName(self):
|
||||
return self.firstName + " " + self.lastName
|
||||
|
||||
marcel = Customer()
|
||||
marcel.firstName = "Marcel"
|
||||
marcel.lastName = "Dempers"
|
||||
marcel.customerID = "h"
|
||||
|
||||
print(marcel.fullName())
|
||||
|
||||
```
|
||||
|
||||
Now how can we create an object with our values without having to set every
|
||||
value individually ?
|
||||
Constructors allow us to initialise our object with some default values.
|
||||
|
||||
```
|
||||
class Customer:
|
||||
def __init__(self, c="", f="", l=""):
|
||||
self.customerID = c
|
||||
self.firstName = f
|
||||
self.lastName = l
|
||||
def fullName(self):
|
||||
return self.firstName + " " + self.lastName
|
||||
|
||||
marcel = Customer("h", "Marcel", "Dempers")
|
||||
|
||||
print(marcel.fullName())
|
||||
|
||||
```
|
||||
|
||||
Let's plug our class into our current application and put it all together:
|
||||
Firstly, we convert all our customers from strings to objects:
|
||||
|
||||
```
|
||||
def getCustomers():
|
||||
customers = {
|
||||
"a": Customer("a","James", "Baker"),
|
||||
"b": Customer("b", "Jonathan", "D"),
|
||||
"c": Customer("c", "Aleem", "Janmohamed"),
|
||||
"d": Customer("d", "Ivo", "Galic"),
|
||||
"e": Customer("e", "Joel", "Griffiths"),
|
||||
"f": Customer("f", "Michael", "Spinks"),
|
||||
"g": Customer("g", "Victor", "Savkov"),
|
||||
"h" : Customer("h", "Marcel", "Dempers")
|
||||
}
|
||||
|
||||
return customers
|
||||
```
|
||||
|
||||
Then we can proceed to get our customer and access their fields:
|
||||
|
||||
```
|
||||
customers = getCustomers()
|
||||
for customerID in customers:
|
||||
print(customers[customerID].fullName())
|
||||
```
|
||||
|
||||
# Docker
|
||||
|
||||
So far we have used our container as a development environment. <br/>
|
||||
Docker allows us to run python in an isolated environment. <br/>
|
||||
Docker also has a concept of stages, I.E Docker Multistage. <br/>
|
||||
|
||||
Therefore we may have a stage for development where we simply mount source code to access a python environment. In addition to this, we may add another layer for debugging where we can install debugger and extra development tools which we
|
||||
do not want in production. <br/>
|
||||
Finally we can create a smaller stage which contains only python + our source which we can run as a runtime image in production.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
FROM python:3.9.6-alpine3.13 as dev
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
FROM python:3.9.6-alpine3.13 as debugging
|
||||
|
||||
# add a debugger
|
||||
|
||||
FROM dev as runtime
|
||||
COPY ./src/ /app
|
||||
|
||||
ENTRYPOINT [ "python", "/app/app.py" ]
|
||||
|
||||
```
|
||||
|
||||
## Building the Container
|
||||
|
||||
```
|
||||
docker build . -t customer-app
|
||||
|
||||
```
|
||||
|
||||
## Running the Container
|
||||
|
||||
```
|
||||
docker run customer-app
|
||||
```
|
12
python/introduction/dockerfile
Normal file
12
python/introduction/dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM python:3.9.6-alpine3.13 as dev
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
FROM python:3.9.6-alpine3.13 as debugging
|
||||
|
||||
# add a debugger
|
||||
|
||||
FROM dev as runtime
|
||||
COPY ./src/ /app
|
||||
|
||||
ENTRYPOINT [ "python", "/app/app.py" ]
|
29
python/introduction/src/app.py
Normal file
29
python/introduction/src/app.py
Normal file
@ -0,0 +1,29 @@
|
||||
class Customer:
|
||||
def __init__(self, c="",f="",l=""):
|
||||
self.customerID = c
|
||||
self.firstName = f
|
||||
self.lastName = l
|
||||
def fullName(self):
|
||||
return self.firstName + " " + self.lastName
|
||||
|
||||
def getCustomers():
|
||||
customers = {
|
||||
"a": Customer("a","James", "Baker"),
|
||||
"b": Customer("b", "Jonathan", "D"),
|
||||
"c": Customer("c", "Aleem", "Janmohamed"),
|
||||
"d": Customer("d", "Ivo", "Galic"),
|
||||
"e": Customer("e", "Joel", "Griffiths"),
|
||||
"f": Customer("f", "Michael", "Spinks"),
|
||||
"g": Customer("g", "Victor", "Savkov"),
|
||||
"h" : Customer("h", "Marcel", "Dempers")
|
||||
}
|
||||
return customers
|
||||
|
||||
def getCustomer(customerID):
|
||||
customer = getCustomers()
|
||||
return customer[customerID]
|
||||
|
||||
|
||||
customers = getCustomers()
|
||||
for customerID in customers:
|
||||
print(customers[customerID].fullName())
|
Loading…
x
Reference in New Issue
Block a user