---
# System prepended metadata

title: Python OOP
tags: [PythonDjango2021, Lesson Notes, Python]

---

###### tags: `Lesson Notes` `Python` `PythonDjango2021`

# Python OOP

## What is OOP?
* Object-Oriented Programming
* It is a specific way of designing a program.  Gives us a way to think of the data that will be in our program as real world objects with properties and behaviors.  And allows them to be passed around throughout the program.
* The 4 Pillars of OOP
    * Inheritance -  Or the Sharing of information
    * Encapsulation - The grouping of information
    * Abstraction - Hiding of information
    * Polymorphism - Redefining of information

## class
Just what is a class?  No not the school type.  Think of a car. There are tons of different types of cars, Make, Model, color, fuel type - but they are all still cars right? 

### Basics of creating a class
So quick and basic we create a class and it will contain objects

* class Car
    * Make
    * Model
    * Color
    * Fuel
Each car or car object that we add to this class will have 4 basic attributes in common, they will have a Make, Model, Color, and a Fuel type.  Now car A might be very different from B but it still works.

### Why?
Ever tried to do a search in an excel spreadsheet for 1 single entry?  You know before the whole ctrl + f thing was brought to our attention.  Well we added filters.  So we would search for all the cars in the spreadsheet that were the Color green for example.  In that list we might have a Honda, Ford, and Toyota for example but they are all green.  

Creating the car class and using OOP we can be sure that when we go to search our database that we can have access to those filters and all the data will have the basic attributes we tell it are required.

## Lets get to the code
Lets build a garden.

### Step 1:
Remember UPER?  Understand, Plan, Execute, Review?
Well step 1 is Understand, but we will include Plan here to because most of the time in understanding we end up planning
* What will each entry have in common?
* What is required?
* What is optional?
* What can we do in the garden?

### Step 2:
:::info
All code show in this can also be found in the following repository on github.  The branch is garden [Repository](https://github.com/Coding-Dojo-ACC/w10d1-python-oop)
:::
Just in case we are building our class on a document that has other code we should start off like this:
```
class Garden:
    pass
```
What this does is keeps the code from breaking.  While we finish getting things ready we can add pass to our code to keep the file it's self passing.

Ok so say we are ready now. Lets fill out the required parts of our garden class as they will always go first. So we will always start our class off like this:
```
class Garden:
    def __init__(self, {add required attributes here})
```
So here is ours:
```
class Garden:
    def __init__(self, gardenName, gardenLocation, sunLevel, gardenSize):
```
Ok so now we have said we are creating a class and naming it Garden,  We are telling our program that we are setting 4 attributes (gardenName, gardenLocation, sunLevel, and gardenSize) as things that all gardens added to the program must have.  This is like our cars.... Every car has a Make, Model, Color, and Fuel Type.  All Gardens will have a name, a location, sun level and size.  If you have ever built a garden you know those are things you need to know.... these will tell you what plants you can grow in your garden and at what time of year.

There may be other things we might want to add but they might be things we don't know right away so lets just leave it at this for now.

Ok now we need to make sure our program understand what those attributes really are... so we add to our code:
```
class Garden:
    def __init__(self, gardenName, gardenLocation, sunLevel, gardenSize):
        self.gardenName = gardenName
        self.gardenLocation = gardenLocation
        self.sunLevel = sunLevel
        self.gardenSize = gardenSize
```
What we just added was simply saying that the attribute of gardenName is self.gardenName.... so later it all matches up. 

But we still haven't added a garden yet.  We also haven't really done anything with these attributes... so lets  make a print statement that when called will tell the user some basic information about our garden:
```
class Garden:
    def __init__(self, gardenName, gardenLocation, sunLevel, gardenSize):
        self.gardenName = gardenName
        self.gardenLocation = gardenLocation
        self.sunLevel = sunLevel
        self.gardenSize = gardenSize

    def gardenInfo(self):
        print(f"{self.gardenName} is located in {self.gardenLocation}.  {self.gardenName} is {self.gardenSize}sq/ft and is in the sun {self.sunLevel}% of the day")
```
Ok so now when we make a garden and the user wants to get some information about it it will print some information.... so lets add a garden and let it print that statement
```
class Garden:
    def __init__(self, gardenName, gardenLocation, sunLevel, gardenSize):
        self.gardenName = gardenName
        self.gardenLocation = gardenLocation
        self.sunLevel = sunLevel
        self.gardenSize = gardenSize

    def gardenInfo(self):
        print(f"{self.gardenName} is located in {self.gardenLocation}.  {self.gardenName} is {self.gardenSize}sq/ft and is in the sun {self.sunLevel}% of the day")

irish = Garden("Irish Gardens", "Berwick, PA", 50, 64)

irish.gardenInfo()
```
Our output would look something like this:
:::success
Irish Gardens is located in Berwick, PA.  Irish Gardens is 64sq/ft and is in the sun 50% of the day
:::

Ok not too bad.  Lets add another garden....and have it's information printed so here are our 2 gardens:
```
irish = Garden("Irish Gardens", "Berwick, PA", 50, 64)
botanical = Garden("Hawaii Botanical Gardens", "Honolulu, HI", 100, 90)
irish.gardenInfo()
botanical.gardenInfo()
```
And our output
:::success
Irish Gardens is located in Berwick, PA.  Irish Gardens is 64sq/ft and is in the sun 50% of the day
Hawaii Botanical Gardens is located in Honolulu, HI.  Hawaii Botanical Gardens is 90sq/ft and is in the sun 100% of the day
:::

Nice!!!  It's all nice and organized and follows a nice pattern so no matter what the user choses they will get the same information.  

Now we need to add somep plants at least to our gardens.  But how we already added 2 gardens if we add to that required list we will break our code right?  Yes you will, but never fear there is a way to add in this case where we don't need to edit our current gardens. 

```
class Garden:
    def __init__(self, gardenName, gardenLocation, sunLevel, gardenSize):
        self.gardenName = gardenName
        self.gardenLocation = gardenLocation
        self.sunLevel = sunLevel
        self.gardenSize = gardenSize
        self.plants = [] #1


    def gardenInfo(self):
        print(f"{self.gardenName} is located in {self.gardenLocation}.  {self.gardenName} is {self.gardenSize}sq/ft and is in the sun {self.sunLevel}% of the day")

    def addPlants(self, plantName): #2
        self.plants.append(plantName)

    def inventory(self): #3
        if self.plants:
            print(f"{self.gardenName} currently has the following plants for you to look and learn about {self.plants}")
        else:
            print(f"{self.gardenName} is currently still being added to and does not have any plants at this time.")
```
So we have added 3 things to our class:
1. self.plants = [] this means we have set an attribute that can be added to a garden and it is currently an empty array.
2. def addPlants allows us to add plants to our inventory at the garden of our chosing.
3. def inventory gives us a way to allow the user to see what plants are at our garden and has a conditional statement written in so no matter what garden they chose something will be returned.  So before we add plants lets make sure our inventory print statement is printing the right one. 

```
irish = Garden("Irish Gardens", "Berwick, PA", 50, 64)
botanical = Garden("Hawaii Botanical Gardens", "Honolulu, HI", 100, 90)
irish.gardenInfo()
botanical.gardenInfo()

irish.inventory()
botanical.inventory()
```
With an output of
:::success
Irish Gardens is located in Berwick, PA.  Irish Gardens is 64sq/ft and is in the sun 50% of the day
Hawaii Botanical Gardens is located in Honolulu, HI.  Hawaii Botanical Gardens is 90sq/ft and is in the sun 100% of the day
Irish Gardens is currently still being added to and does not have any plants at this time.
Hawaii Botanical Gardens is currently still being added to and does not have any plants at this time.
:::

Looks like so far we are on the right track. Ok lets add some plants.
```
irish = Garden("Irish Gardens", "Berwick, PA", 50, 64)
botanical = Garden("Hawaii Botanical Gardens", "Honolulu, HI", 100, 90)

irish.addPlants("Tulips")
irish.addPlants("Roses")

irish.gardenInfo()
botanical.gardenInfo()

irish.inventory()
botanical.inventory()
```
And the output:
:::success
Irish Gardens is located in Berwick, PA.  Irish Gardens is 64sq/ft and is in the sun 50% of the day
Hawaii Botanical Gardens is located in Honolulu, HI.  Hawaii Botanical Gardens is 90sq/ft and is in the sun 100% of the day
Irish Gardens currently has the following plants for you to look and learn about ['Tulips', 'Roses']
Hawaii Botanical Gardens is currently still being added to and does not have any plants at this time.
:::

So you can see both in the code and the out put that we have add 2 plants to Irish Gardens but nothing to Hawaii Botanical Gardens.  And the outputs reflect this. 

Say some Gardens offer tours.  How would we let the user know that they are offered?  Instead of adding an array lets add a string. 
```
class Garden:
    def __init__(self, gardenName, gardenLocation, sunLevel, gardenSize):
        self.gardenName = gardenName
        self.gardenLocation = gardenLocation
        self.sunLevel = sunLevel
        self.gardenSize = gardenSize
        self.plants = []
        self.tours = ""


----------------

    def addTours(self, toursAvail):
        self.tours = toursAvail

    def tourInfo(self):
        if self.tours:
            print(f"{self.gardenName} currently has {self.tours} upon request.  Please call for more information")
        else:
            print(f"{self.gardenName} currently does not offer tours")
```
So this is what we added for tours.... and our output:
:::success
Irish Gardens is located in Berwick, PA.  Irish Gardens is 64sq/ft and is in the sun 50% of the day
Hawaii Botanical Gardens is located in Honolulu, HI.  Hawaii Botanical Gardens is 90sq/ft and is in the sun 100% of the day
Irish Gardens currently has the following plants for you to look and learn about ['Tulips', 'Roses']
Hawaii Botanical Gardens is currently still being added to and does not have any plants at this time.
Irish Gardens currently does not offer tours
Hawaii Botanical Gardens currently has 3 different types of tours available upon request.  Please call for more information
:::

### Step 3:
Review.

You should really be doing this every step or so like I did above.  Every time we added  a new thing to our code I tested to make sure it worked.  Added the code to add plants but tested before I actually added any.  Things like that.  Testing along the way helps you in the long run as you can be sure that as soon as it fails you know it was the last thing you edited.  These make good commit point too. 