marceldempers 47b99f55e5
Merge pull request #203 from marcel-dempers/dependabot/pip/python/introduction/part-4.http/src/flask-2.3.2
Bump flask from 2.0.2 to 2.3.2 in /python/introduction/part-4.http/src
2023-10-04 21:58:46 +11:00
..
2021-09-01 22:11:28 +10:00
2021-08-27 19:39:47 +10:00
2021-08-21 09:19:15 +10:00
2021-08-21 09:19:15 +10:00

Introduction to Learning Python

Best place to start is the documentation www.python.org

Download Python 3

Docker images for Python on Docker Hub

Run Python in Docker

We can also run python in a docker container:

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.
It allows us to execute a block of code
You want to give your function a single purpose
Functions can have an input and return an output
Well thought out functions makes it easier to write tests

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.
Variables take up space in memory, so we want to keep it minimal.
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.
Let's use it and showcase control flows to create logic!

Control flows allow us to add "rules" to our code.
"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.
What if we wanted to return more than one customer.
In real world systems, we may return database records.
This is where we start looking at arrays, lists, dictionaries etc.

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.
Arrays are fixed in size, notice we cannot just access a record :

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.
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.
With Dictionaries, we store key value pairs. So the key, may be our customer ID.

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.
There are two primitive loops in Python
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.
Customers can have a firstname, lastname, and more properties.

For this purpose we'd like to group some variables into a single variable.
This is what classes allows us to do.
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.
Docker allows us to run python in an isolated environment.
Docker also has a concept of stages, I.E Docker Multistage.

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.
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