From ea0634f207f221d566b72a424d5f2b3656a8ee67 Mon Sep 17 00:00:00 2001 From: marcel-dempers Date: Sat, 21 Aug 2021 09:19:15 +1000 Subject: [PATCH 1/2] add files for python --- python/introduction/README.md | 434 +++++++++++++++++++++++++++++++++ python/introduction/dockerfile | 12 + python/introduction/src/app.py | 33 +++ 3 files changed, 479 insertions(+) create mode 100644 python/introduction/README.md create mode 100644 python/introduction/dockerfile create mode 100644 python/introduction/src/app.py diff --git a/python/introduction/README.md b/python/introduction/README.md new file mode 100644 index 0000000..5656aba --- /dev/null +++ b/python/introduction/README.md @@ -0,0 +1,434 @@ +# Introduction to Learning Python + +Best place to start is the documentation [www.python.org](https:#www.python.org/)
+ +Download [Python 3](https:#www.python.org/downloads/)
+ +Docker images for Python on [Docker Hub](https:#hub.docker.com/_/python)
+ + +# 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 +``` \ No newline at end of file diff --git a/python/introduction/dockerfile b/python/introduction/dockerfile new file mode 100644 index 0000000..d7a8337 --- /dev/null +++ b/python/introduction/dockerfile @@ -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" ] diff --git a/python/introduction/src/app.py b/python/introduction/src/app.py new file mode 100644 index 0000000..a26d2c9 --- /dev/null +++ b/python/introduction/src/app.py @@ -0,0 +1,33 @@ +def getCustomer(customerID): + customer = getCustomers() + return customer[customerID] + +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 + +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 + +customers = getCustomers() +for customerID in customers: + print(customers[customerID].fullName()) + + + + From fdeec671dea9d37c27e4ffb9f1c7862193c7541e Mon Sep 17 00:00:00 2001 From: marcel-dempers Date: Fri, 27 Aug 2021 19:39:47 +1000 Subject: [PATCH 2/2] updates --- python/introduction/src/app.py | 62 ++++++++++++++++------------------ 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/python/introduction/src/app.py b/python/introduction/src/app.py index a26d2c9..9c6c101 100644 --- a/python/introduction/src/app.py +++ b/python/introduction/src/app.py @@ -1,33 +1,29 @@ -def getCustomer(customerID): - customer = getCustomers() - return customer[customerID] - -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 - -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 - -customers = getCustomers() -for customerID in customers: - print(customers[customerID].fullName()) - - - - +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()) \ No newline at end of file