Tuesday, January 26, 2021

Python - Class (Day 17)

This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp 

I will post some notes to motivate myself to finish this challenge.


How to create a class



Class is a Blueprint.
It starts with 'class' keyword, followed with class name.

Ex:
# Define a class
class User:
    # pass statement
    pass


# Use class to create an object
user = User()



Naming Convention


PascalCase (HelloWorld)

It is used by Python Class name. Reference.

# Class
class MyClass:
    pass


snake_case (hellow_world)

It is used by Python function name and variable name. Reference

# Custom Functions
def my_function():
    print("this is my function")


# Variables
int_val = 1


camelCase (helloWorld)


Used it in Python only if needed (backwards compatibility).  Reference


What is pass statement



Under 'How to create a class' section, what will it happen if we remove pass statement?
# Define a class
class User:
    # pass statement
    # pass


# Use class to create an object
user = User()


Error: 
user_1 = User()
^ IndentationError: expected an indented block


In python, empty code is not allowed in loops, function, class, and if else condition. 

Therefore, you will get an error if you run previous example since interpreter think you forget to give them some indents.

The solution is to add pass statement. 'pass' statement is used as a placeholder for future code. 
Also, note that interpreter will ignore comments totally, but 'pass' is not ignored.


Constructor



We can setup attributes of object after creating. This way is error prone because we might have typo.

Ex (Without Constructor):

# Define a Class without Constructor
class User:
    pass

# Using Class to create an object
user = User()
# set object attribute 'id' to 001
user.id = "001"

# if we have a typo (change i to l) from the previous instruction
# then user.id will not be setup
# and it is hard to debug
# user.ld = "001"

print(f"user id: { user.id}")


Constructor is also called initializer.
The recommended way to use.

Ex:

# Define a Class
class User:
# Constructor
    def __init__(self, id):
        self.id = id

# Using Class to create an object
user = User(1)

print(f"user id: {user.id}")


Then we just need to pass parameters to Class when creating.


What is 'self' in constructor?



self represents the instance of the class.
By using the 'self' keyword we can access the attributes and methods of the class in python. 


Adding methods



Ex:
# Define a class
class User:
    # Constructor
# Init attributes
    def __init__(self, user_id, username):
        self.id = user_id
        self.username = username
        self.followers = 0
        self.following = 0

    # Methods
    def follow(self, user):
        user.followers += 1
        self.following += 1

# Create objects
user_1 = User("001", "Frank")
user_2 = User("002", "Jake")
user_3 = User("003", "Mary")
# Call methods
user_2.follow(user_1)
user_3.follow(user_1)

print(
    f"user 1: {user_1.id} - {user_1.username} -
{user_1.followers} - {user_1.following}"
)
print(
    f"user 2: {user_2.id} - {user_2.username} -
{user_2.followers} - {user_2.following}"
)
print(
    f"user 3: {user_3.id} - {user_3.username} -
{user_3.followers} - {user_3.following}"
)


Result:

user 1: 001 - Frank - 2 - 0
user 2: 002 - Jake - 0 - 1 user 3: 003 - Mary - 0 - 1




Challenge - Build a Quiz Project


Build a quiz project with OOP style.
* Question class:
    attributes
        text
        answer
* QuizBrain class
    attributes
        score
        question_number
        question_list
    methods
        still_has_questions
        next_question
        check_answer

Ex: 
data.py (Quiz Resource)
question_data = [
    {
        "category": "Entertainment: Video Games",
        "type": "boolean",
        "difficulty": "easy",
        "question": "Peter Molyneux was the founder of Bullfrog
Productions.",
        "correct_answer": "True",
        "incorrect_answers": ["False"],
    },
    {
        "category": "Vehicles",
        "type": "boolean",
        "difficulty": "hard",
        "question": "In 1993 Swedish car manufacturer Saab experimented
with replacing the steering wheel
with a joystick in a Saab 9000.",
        "correct_answer": "True",
        "incorrect_answers": ["False"],
    },
    {
        "category": "General Knowledge",
        "type": "boolean",
        "difficulty": "easy",
        "question": "French is an official language in Canada.",
        "correct_answer": "True",
        "incorrect_answers": ["False"],
    },
]


question_model.py
class Question:
    """Question"""

    def __init__(self, text, answer):
        self.text = text
        self.answer = answer


question_model.py
class QuizBrain:
    """QuizBrain"""

    # Constructor
    def __init__(self, question_list):
        self.question_list = question_list
        self.score = 0
        self.question_number = 0

    def still_has_questions(self):

        if self.question_number >= len(self.question_list):
            return False

        return True

    def next_question(self):

        # Increase question number
        self.question_number += 1

        # Use (question_number - 1) as array index
        question = self.question_list[self.question_number - 1]

        # Ask user input
        user_answer = input(
            f"Q.{self.question_number }: {question.text} (True/False): "
        )

        # Determine the answer
        self.check_answer(user_answer, question.answer)

    def check_answer(self, user_answer, correct_answer):

        if user_answer == correct_answer:
            print("Right!")
            self.score += 1
        else:
            print("Opps")

        print(f"The correct answer is {correct_answer}")
        print(f"The current score is:
{self.score}/{self.question_number}")


With OOP design, QuizBrain did not need to generate the quiz list. The quiz list is passed by caller (main.py). So it is reusable if the quiz resource are coming from different website with different format.

Caller (main.py) just need to format it and pass it when initializing QuizBrain. Then QuizBrain can be reused by different program. That is the beauty of OOP.

Monday, January 25, 2021

Python - Object Oriented Programming (Day 16)

This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp 

I will post some notes to motivate myself to finish this challenge.


Procedural Programming



Program works from top to bottom and jump into a function if needed.

Procedural Programming may be the first programming paradigm that a new developer will learn. Fundamentally, the procedural code is the one that directly instructs a device on how to finish a task in logical steps. (Refer to this Article)

When program get bigger and bigger, it will become complex since a function will lead to another function, and so on. The relationship between functions will be super complicated, and it is hard to memorize and maintain not only by the developers who take over this task, but also by the owner who built this program.


Object Oriented Programming



According to 'Procedural Programming', if we can break the functionalities into modules, then the complexity will reduce. Also, each module is reusable. You even don't need to understand the detail implementation of each modules. You just need to know how to use those modules.

For example, if we can split the process of building the self-driving car into some components such as camera, land detection, navigation, battery control, then the whole team (total 100 persons) can be split into four teams (20 persons per team) to take care of each parts simultaneously.  Also, once the navigation functionality from the self-driving car is done. Then we can easily migrate it to other projects such as drone.

It is the same concept such as "You are a manager of an restaurant". You don't need to tell Chef how to cook because Chef already have this skill. And you just need to let Chef know when to cook.


Class and Object



In OOP, we try to model real-life objects, and those objects have things and can do things (blueprint).

The things they have are their attributes.
The things they can do are called methods.

For example, we can model a waiter as the following sample code.
class Waiter:
    # has (Attributes)
    is_holding_plate = True
    table_responsible = [1, 2, 3]

    # does (Methods)
    def take_order(table, order):
        print(f"taking order {order} for table {table}")

    def take_payment(amount):
        print(f"take payment {amount}")


Once we have defined this, then we can have multiple objects generated from this blueprint.
In OOP, we call this blueprint a Class.

Ex: 
# Waiter is a class
# waiter_john and waiter_jake are objects
waiter_john = Waiter()
waiter_jake = Waiter()



Turtle Graphic



We will use Turtle Graphic to learn how to construct objects and accessing attributes and methods.

Ex:

from turtle import Turtle, Screen

# Construct Object
my_turtle = Turtle()
print(my_turtle)

# Accessing Attributes
my_screen = Screen()
print(my_screen.canvheight)

# Access Methods
my_turtle.shape("turtle")



How to add a Python Packages to our project?



For example, if we want to use prettytable package in our project, how do we install it?

1. Go to Python Package Index website


2. Search 'prettytable' and select the first one


3. Run the following command


python -m pip install -U prettytable


4. Read 'prettytable' document to understand how to use this package.

Ex:

from prettytable import PrettyTable

x = PrettyTable()
x.field_names = ["Column 1", "Column 2"]
x.add_row(["Demo 1", 123])
x.add_row(["Demo 2", 4565])
print(x)

Result:

+----------+----------+
| Column 1 | Column 2 | +----------+----------+ | Demo 1 | 123 | | Demo 2 | 4565 | +----------+----------+



Challenge - Coffee Machine (Revise the day 15 challenge to OOP style)



Build a Coffee Machine Program which support some functionalities below:
    * Prompt user by asking “What would you like? (espresso/latte/cappuccino):
    * Turn off the Coffee Machine by entering “off” to the prompt
    * Print report.
    * Check resources sufficient?
    * Process coins
    * Check transaction successful?
    * Make Coffee.

Ex: 
from menu import Menu
from coffee_maker import CoffeeMaker
from money_machine import MoneyMachine

menu = Menu()
coffee_maker = CoffeeMaker()
money_machine = MoneyMachine()

is_machine_on = True

while is_machine_on:
    user_select = input(f"What would you like? { menu.get_items()}:")

    if user_select == "report":
        coffee_maker.report()
        money_machine.report()
    elif user_select == "off":
        is_machine_on = False
    else:
        drink = menu.find_drink(user_select)

        if drink is None:
            print("Invalid Input")
        else:
            if coffee_maker.is_resource_sufficient(drink):
                if money_machine.make_payment(drink.cost):
                    coffee_maker.make_coffee(drink)


Saturday, January 23, 2021

Python - Setup local environment (Day 15)

This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp 

I will post some notes to motivate myself to finish this challenge.


Install Python





Install PyCharm (Option 1)



* Spell check
* More Space (View two files side by side)
* Built-In Linter
* Local History
* View Structure
* Refactor Rename


Install VS Code and its extension (Option 2)



* Formatting: Formatting makes code easier to read by human beings by applying specific rules and conventions for line spacing, indents, spacing around operators, and so on
    $ pip install autopep8

* Linting: Linting highlights syntactical and stylistic problems in your Python source code
     $ pip install pylint


Challenge - Coffee Machine



Build a Coffee Machine Program which support some functionalities below:
    * Prompt user by asking “What would you like? (espresso/latte/cappuccino):
    * Turn off the Coffee Machine by entering “off” to the prompt
    * Print report.
    * Check resources sufficient?
    * Process coins
    * Check transaction successful?
    * Make Coffee.

Ex: 
resource.py
MENU = {
    "espresso": {
        "ingredients": {
            "water": 50,
            "coffee": 18,
        },
        "cost": 1.5,
    },
    "latte": {
        "ingredients": {
            "water": 200,
            "milk": 150,
            "coffee": 24,
        },
        "cost": 2.5,
    },
    "cappuccino": {
        "ingredients": {
            "water": 250,
            "milk": 100,
            "coffee": 24,
        },
        "cost": 3.0,
    }
}

main.py
from resource import MENU

# Constants
QUARTER = 0.25
DIME = 0.1
NICKEL = 0.05
PENNY = 0.01


def print_report(resource, profit):
    """Print current resource and profit"""
    # Resource
    for key in resource:
        if key == "Coffee":
            print(f"{key}: {resource[key]}g")
        else:
            print(f"{key}: {resource[key]}ml")

    # Profit
    print(f"Money: ${profit}")


def is_sufficient_resource(coffee_item_ingredients, resource):
    """Return True/False to determine whether the resouce is
enough to make a specific coffee"""
    if "water" in coffee_item_ingredients:
        if coffee_item_ingredients["water"] > resource["Water"]:
            print(f"Sorry there is not enough water")

            return False

    if "milk" in coffee_item_ingredients:
        if coffee_item_ingredients["milk"] > resource["Milk"]:
            print(f"Sorry there is not enough milk")

            return False

    if "coffee" in coffee_item_ingredients:
        if coffee_item_ingredients["coffee"] > resource["Coffee"]:
            print(f"Sorry there is not enough coffee")

            return False

    return True


def collect_coins():
    """Ask user to input and return the total value of coins"""
    print("Please insert coins.")
    q = int(input("how many quarters?:"))
    d = int(input("how many dimes?:"))
    n = int(input("how many nickles?:"))
    p = int(input("how many pennies?:"))

    return q * QUARTER + d * DIME + n * NICKEL + p * PENNY


def is_enough_money(money, item):
    """Return True/False to determine whether it is enough money to
make a specific coffee"""
    change = money - item["cost"]

    if change >= 0:
        print(f"Here is ${change} in change.")

        return True
    else:
        print("Sorry that's not enough money. Money refunded.")

        return False


def make_coffee(user_select, coffee_item_ingredients, resource):

    if "water" in coffee_item_ingredients:
        resource["Water"] = resource["Water"] -
coffee_item_ingredients["water"]
    if "milk" in coffee_item_ingredients:
        resource["Milk"] = resource["Milk"] -
coffee_item_ingredients["milk"]
    if "coffee" in coffee_item_ingredients:
        resource["Coffee"] = resource["Coffee"] -
coffee_item_ingredients["coffee"]

    print(f"Here is your {user_select} ☕️. Enjoy!")

    return resource


def coffee_machine():
    profit = 0
    resource = {
        "Water": 300,
        "Milk": 200,
        "Coffee": 100,
    }

    is_machine_on = True

    while is_machine_on:

        user_select = input("What would you like?
(espresso/latte/cappuccino):")
        if user_select == "report":
            print_report(resource, profit)
        elif user_select == "off":
            is_machine_on = False
        else:
            coffee_item = MENU[user_select]

            # Enough resouce?
            if is_sufficient_resource(coffee_item["ingredients"],
resource):
                money = collect_coins()

                # Enough money?
                if is_enough_money(money, coffee_item):
                    # Update profit
                    profit += coffee_item["cost"]

                    # Make coffee and Update resource
                    resource = make_coffee(
                        user_select, coffee_item["ingredients"],
resource
                    )


coffee_machine()


Friday, January 22, 2021

Python - Project - Higher or Lower game (Day 14)

This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp 

I will post some notes to motivate myself to finish this challenge.


Higher or Lower Game



Build a simple higher or lower game such as Reference.

    * Pick up accounts randomly
    * Format the account information
    * Ask user for a guess
    * Once user guess correctly, make account position b (compare B) 
       become position a (Compare A) on next game
    * Score keeping

Ex: 
game_data.py

data = [
    {
        'name': 'Instagram',
        'follower_count': 346,
        'description': 'Social media platform',
        'country': 'United States'
    },
...
]


main.py
import random
from game_data import data


def getAccountInfo(account):
    return account["name"] + ", " + account["description"] + ", " +
account["country"]


def getRandomAccount():
    return random.choice(data)


def setupAccountB(compare):
    compare["B"] = getRandomAccount()
    while compare["B"] == compare["A"]:
        compare["B"] = getRandomAccount()

    return compare


def game():
    score = 0

    # Init Compare Dictionary
    compare = {
        "A": getRandomAccount(),
    }

    game_end = False

    while not game_end:
        # Update compare dictionary by randomly picking company B
# from accounts
        compare = setupAccountB(compare)

        print(f"Compare A: { getAccountInfo(compare['A'])}")
        print("V.S.")
        print(f"Compare B: { getAccountInfo(compare['B'])}")

        # Ask user input
        guess = input("Who has more followers? Type 'A' or 'B': ")

        # Right Guess
        if (
            guess == "A"
            and compare["A"]["follower_count"] >
compare["B"]["follower_count"]
        ) or (
            guess == "B"
            and compare["B"]["follower_count"] >
compare["A"]["follower_count"]
        ):
            score += 1
            print(f"You're right! Current score: { score }.\n")

            # This is an requirement to make B to be a Compare A on
# next run
            compare["A"] = compare["B"]

            continue

        # Wrong Guess, and then terminate this game
        game_end = True

    print(f"Sorry, that's wrong. Final score: { score}")


game()


Thursday, January 21, 2021

Python - Find and Fix errors (Day 13)

This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp 

I will post some notes to motivate myself to finish this challenge.


Tip 1 - Describe your problems



Analyze what the program/function is built for, and check whether the output is correct or not.

Ex:
def my_function():
    """Loop through 1 to 20 and print "You got it" if it is 20"""

    for i in range(1, 20):
        if i == 20:
            print("You got it")


my_function()

Result:

# No Output


From this example, my_function will loop through 1 to 20, and once i is 20, it should print the log 'You got it'. So our assumption is that i will be 20 in the end of the loop. However, after running this code, you noticed that there is no any output. So there must be something wrong.

This is a bug that we misuse range function. Reference


Tip 2 - Reproduce



Sometimes, the reproduced rate is not 100%, which will make you hard to debug it. If you cannot reproduce it, then you don't have enough knowledge/clue to resolve the issue.

Ex:
from random import randint

dice_imgs = ["h", "e", "l", "l", "o"]
dice_num = randint(1, 5)
print(dice_imgs[dice_num])

Result:

IndexError: list index out of range


For this example, the dice_num will be 1 to 5. Then dice_imgs[5] will throw erros.


Tip 3 - Play Computer and Evaluate each line



Ex:
year = int(input("What's your year of birth?"))
if year > 1980 and year < 1994:
    print("You are a millennial.")
elif year > 1994:
    print("You are a Gen Z.")

Result:

# No Output if the input is 1994


Try to use 1994 or some number less than 1980 as an input to run this function through your brain and finger.


Tip 4 - Fixing Errors and Watching for Red Underlines



Some IDE will help you to find out the syntax errors.

Ex:
age = input("How old are you?")
if age > 18:
print("You can drive at age {age}.")

Result:

IndentationError: expected an indented block


There are three issues in this example.
First, before running this code, the ide will show the Red Underlines to warn you.
Second, once you run it, the console will log some error, and you will have clue for how to resolve it.
Third, it misuse the f-string.


Tip 5 - print() is your friend



Ex:
pages = 0
word_per_page = 0
pages = int(input("Number of pages: "))
word_per_page == int(input("Number of words per page: "))
total_words = pages * word_per_page
print(total_words)

Result:

0


Since there is a typo in this example, and it is hard to detect. Print out the variables value can save your time.


Tip 6 - Use a debugger

Tip 7 - Take a break



Tip 8 - Ask a friend



Fresh eyes or different way to look through the issues.


Tip 9 - Run Often



Once you finish a small chunk of feature, run it and test it directly. Don't wait until you finish the whole feature.


Tip 10 - Ask StackOverflow