Class and Object Terms

The foundations of Object-Oriented Programming is defining a Class

  • In Object-Oriented Programming (OOP), a class is a blueprint for creating an Object. (a data structure). An Object is used like many other Python variables.
  • A Class has ...
    • a collection of data, these are called Attributes and in Python are pre-fixed using the keyword self
    • a collection of Functions/Procedures. These are called *Methods when they exist inside a Class definition.
  • An Object is created from the Class/Template. Characteristics of objects ...
    • an Object is an Instance of the Class/Template
    • there can be many Objects created from the same Class
    • each Object contains its own Instance Data
    • the data is setup by the Constructor, this is the "init" method in a Python class
    • all methods in the Class/Template become part of the Object, methods are accessed using dot notation (object.method())
  • A Python Class allow for the definition of @ decorators, these allow access to instance data without the use of functions ...
    • @property decorator (aka getter). This enables developers to reference/get instance data in a shorthand fashion (object.name versus object.get_name())
    • @name.setter decorator (aka setter). This enables developers to update/set instance data in a shorthand fashion (object.name = "John" versus object.set_name("John"))
    • observe all instance data (self._name, self.email ...) are prefixed with "", this convention allows setters and getters to work with more natural variable name (name, email ...)

Class and Object Code

# Werkzeug is a collection of libraries that can be used to create a WSGI (Web Server Gateway Interface)
# A gateway in necessary as a web server cannot communicate directly with Python.
# In this case, imports are focused on generating hash code to protect passwords.
from werkzeug.security import generate_password_hash, check_password_hash
import json

# Define a User Class/Template
# -- A User represents the data we want to manage
class User:    
    # constructor of a User object, initializes the instance variables within object (self)
    def __init__(self, name, uid, password):
        self._name = name    # variables with self prefix become part of the object, 
        self._uid = uid
        self.set_password(password)

    # a name getter method, extracts name from object
    @property
    def name(self):
        return self._name
    
    # a setter function, allows name to be updated after initial object creation
    @name.setter
    def name(self, name):
        self._name = name
    
    # a getter method, extracts email from object
    @property
    def uid(self):
        return self._uid
    
    # a setter function, allows name to be updated after initial object creation
    @uid.setter
    def uid(self, uid):
        self._uid = uid
        
    # check if uid parameter matches user id in object, return boolean
    def is_uid(self, uid):
        return self._uid == uid
    
    @property
    def password(self):
        return self._password[0:10] + "..." # because of security only show 1st characters

    # update password, this is conventional setter
    def set_password(self, password):
        """Create a hashed password."""
        self._password = generate_password_hash(password, method='sha256')

    # check password parameter versus stored/encrypted password
    def is_password(self, password):
        """Check against hashed password."""
        result = check_password_hash(self._password, password)
        return result
    
    # output content using str(object) in human readable form, uses getter
    def __str__(self):
        return f'name: "{self.name}", id: "{self.uid}", psw: "{self.password}"'

    # output command to recreate the object, uses attribute directly
    def __repr__(self):
        return f'Person(name={self._name}, uid={self._uid}, password={self._password})'


# tester method to print users
def tester(users, uid, psw):
    result = None
    for user in users:
        # test for match in database
        if user.uid == uid and user.is_password(psw):  # check for match
            print("* ", end="")
            result = user
        # print using __str__ method
        print(str(user))
    return result
        

# place tester code inside of special if!  This allows include without tester running
if __name__ == "__main__":

    # define user objects
    u1 = User(name='Thomas Edison', uid='toby', password='123toby')
    u2 = User(name='Nicholas Tesla', uid='nick', password='123nick')
    u3 = User(name='Alexander Graham Bell', uid='lex', password='123lex')
    u4 = User(name='Eli Whitney', uid='eli', password='123eli')
    u5 = User(name='Hedy Lemarr', uid='hedy', password='123hedy')

    # put user objects in list for convenience
    users = [u1, u2, u3, u4, u5]

    # Find user
    print("Test 1, find user 3")
    u = tester(users, u3.uid, "123lex")


    # Change user
    print("Test 2, change user 3")
    u.name = "John Mortensen"
    u.uid = "jm1021"
    u.set_password("123qwerty")
    u = tester(users, u.uid, "123qwerty")


    # Make dictionary
    ''' 
    The __dict__ in Python represents a dictionary or any mapping object that is used to store the attributes of the object. 
    Every object in Python has an attribute that is denoted by __dict__. 
    Use the json.dumps() method to convert the list of Users to a JSON string.
    '''
    print("Test 3, make a dictionary")
    json_string = json.dumps([user.__dict__ for user in users]) 
    print(json_string)

    print("Test 4, make a dictionary")
    json_string = json.dumps([vars(user) for user in users]) 
    print(json_string)
Test 1, find user 3
name: "Thomas Edison", id: "toby", psw: "sha256$hcc..."
name: "Nicholas Tesla", id: "nick", psw: "sha256$C3R..."
* name: "Alexander Graham Bell", id: "lex", psw: "sha256$pWz..."
name: "Eli Whitney", id: "eli", psw: "sha256$WLY..."
name: "Hedy Lemarr", id: "hedy", psw: "sha256$H8q..."
Test 2, change user 3
name: "Thomas Edison", id: "toby", psw: "sha256$hcc..."
name: "Nicholas Tesla", id: "nick", psw: "sha256$C3R..."
* name: "John Mortensen", id: "jm1021", psw: "sha256$31E..."
name: "Eli Whitney", id: "eli", psw: "sha256$WLY..."
name: "Hedy Lemarr", id: "hedy", psw: "sha256$H8q..."
Test 3, make a dictionary
[{"_name": "Thomas Edison", "_uid": "toby", "_password": "sha256$hccwkCYaMa6g9lJf$7b92ca6451e952c55402b653eb3dfba09e0f8ab48d033321c09c0c9a5c9903cf"}, {"_name": "Nicholas Tesla", "_uid": "nick", "_password": "sha256$C3RZM6VbXPovQjvn$dc5bbfa0bae46086296775a2113e1422600ab036e8ec79aa5a0b03964cfd24ff"}, {"_name": "John Mortensen", "_uid": "jm1021", "_password": "sha256$31E36pcsGQwUDQrF$4f08c997dba4c5d410f7e6be7503f08fda872372ea40f20b97b976f3f7234435"}, {"_name": "Eli Whitney", "_uid": "eli", "_password": "sha256$WLYgiqEoJS6dUFz2$7bc36a8aed6f0b6abfb8e3deccc213ac70fb9a89988f3ddddf88fa685b1bbf10"}, {"_name": "Hedy Lemarr", "_uid": "hedy", "_password": "sha256$H8qcF7FQAOvWZinK$f2b435bd2f4a4cb6ab89274f253d964cb7a61c90c2a19bdadfe772a1a897dda5"}]
Test 4, make a dictionary
[{"_name": "Thomas Edison", "_uid": "toby", "_password": "sha256$hccwkCYaMa6g9lJf$7b92ca6451e952c55402b653eb3dfba09e0f8ab48d033321c09c0c9a5c9903cf"}, {"_name": "Nicholas Tesla", "_uid": "nick", "_password": "sha256$C3RZM6VbXPovQjvn$dc5bbfa0bae46086296775a2113e1422600ab036e8ec79aa5a0b03964cfd24ff"}, {"_name": "John Mortensen", "_uid": "jm1021", "_password": "sha256$31E36pcsGQwUDQrF$4f08c997dba4c5d410f7e6be7503f08fda872372ea40f20b97b976f3f7234435"}, {"_name": "Eli Whitney", "_uid": "eli", "_password": "sha256$WLYgiqEoJS6dUFz2$7bc36a8aed6f0b6abfb8e3deccc213ac70fb9a89988f3ddddf88fa685b1bbf10"}, {"_name": "Hedy Lemarr", "_uid": "hedy", "_password": "sha256$H8qcF7FQAOvWZinK$f2b435bd2f4a4cb6ab89274f253d964cb7a61c90c2a19bdadfe772a1a897dda5"}]

Hacks

Add new attributes/variables to the Class. Make class specific to your CPT work.

  • Add classOf attribute to define year of graduation
    • Add setter and getter for classOf
  • Add dob attribute to define date of birth
    • This will require investigation into Python datetime objects as shown in example code below
    • Add setter and getter for dob
  • Add instance variable for age, make sure if dob changes age changes
    • Add getter for age, but don't add/allow setter for age
  • Update and format tester function to work with changes

Start a class design for each of your own Full Stack CPT sections of your project

  • Use new code cell in this notebook
  • Define init and self attributes
  • Define setters and getters
  • Make a tester

Start Code for Hacks

from datetime import date

def calculate_age(born):
    today = date.today()
    return today.year - born.year - ((today.month, today.day) < (born.month, born.day))

dob = date(2004, 12, 31)
age = calculate_age(dob)
print(age)
18

TripleAJ Arcade Backend Functionality

import json

class Gamer:
    def __init__(self, ReactionTime, HiLo, Pong, Snake, Tokens):
        self._ReactionTime = ReactionTime
        self._HiLo = HiLo
        self._Pong = Pong
        self._Snake = Snake
        self._Tokens = HiLo + Pong + Snake + ReactionTime
    
    # Getters:
    @property
    def ReactionTime(self):
        return self._ReactionTime
    
    @property
    def HiLo(self):
        return self._HiLo

    @property
    def Pong(self):
        return self._Pong   

    @property
    def Snake(self):
        return self._Snake 
    
    @property
    def Tokens(self):
        return self._Tokens
   
    # Setters    
    @ReactionTime.setter
    def Weight(self, ReactionTime):
        self._ReactionTime = ReactionTime 

    @HiLo.setter
    def HiLo(self, HiLo):
        self._Bench = HiLo

    @Pong.setter
    def Pong(self, Pong):
        self._Pong = Pong

    @Snake.setter
    def Snake(self, Snake):
        self._Press = Snake

    @Tokens.setter
    def Tokens(self, Tokens):
        self.Pushup = Tokens

    @property
    def dictionary(self):
        dict = {
            "ReactionTime" : self.ReactionTime,
            "HiLo" : self.HiLo,
            "Pong" : self.Pong,
            "Snake" : self.Snake,
            "Tokens" : self.Tokens,
        }
        return dict

    def __str__(self):
        return json.dumps(self.dictionary)
    
    def __repr__(self):
        return f'Gamer(ReactionTime={self._ReactionTime}, HiLo={self._HiLo}, Pong={self._Pong}, Snake={self._Snake}, Tokens={self._Tokens})'

Gamer1 = Gamer(15, 22, 10, 4, 51)
Gamer2 = Gamer(22, 11, 9, 44, 87)
print("Gamer 1 ReactionTime Score ", Gamer1.ReactionTime, "points")
print("Gamer 1 HiLo Score ", Gamer1.HiLo, "points")
print("Gamer 1 Pong Score ", Gamer1.Pong, "points")
print("Gamer 1 Snake Score ", Gamer1.Snake, "points")
print("Gamer 1 Total ", Gamer1.Tokens, "tokens")
print("Gamer 2 ReactionTime Score ", Gamer2.ReactionTime, "points")
print("Gamer 2 HiLo Score ", Gamer2.HiLo, "points")
print("Gamer 2 Pong Score ", Gamer2.Pong, "points")
print("Gamer 2 Snake Score ", Gamer2.Snake, "points")
print("Gamer 2 Total ", Gamer2.Tokens, "tokens")
print(Gamer1)
print(Gamer2)
Gamer 1 ReactionTime Score  15 points
Gamer 1 HiLo Score  22 points
Gamer 1 Pong Score  10 points
Gamer 1 Snake Score  4 points
Gamer 1 Total  51 tokens
Gamer 2 ReactionTime Score  22 points
Gamer 2 HiLo Score  11 points
Gamer 2 Pong Score  9 points
Gamer 2 Snake Score  44 points
Gamer 2 Total  86 tokens
{"ReactionTime": 15, "HiLo": 22, "Pong": 10, "Snake": 4, "Tokens": 51}
{"ReactionTime": 22, "HiLo": 11, "Pong": 9, "Snake": 44, "Tokens": 86}

Hacks

import json

class User:
    def __init__(self, Name, UID, Password, ClassOf, DOB):
        self._Name = Name
        self._UID = UID
        self._Password = Password
        self._ClassOf = ClassOf
        self._DOB = DOB
    
    # Getters:
    @property
    def Name(self):
        return self._Name
    
    @property
    def UID(self):
        return self._UID

    @property
    def Password(self):
        return self._Password

    @property
    def ClassOf(self):
        return self._ClassOf
    
    @property
    def DOB(self):
        return self._DOB
   
    # Setters    
    @Name.setter
    def Name(self, Name):
        self._Name = Name 

    @UID.setter
    def UID(self, UID):
        self._UID = UID

    @Password.setter
    def Password(self, Password):
        self._Password = Password

    @ClassOf.setter
    def ClassOF(self, ClassOf):
        self._ClassOf = ClassOf

    @DOB.setter
    def DOB(self, DOB):
        self.DOB = DOB

    @property
    def dictionary(self):
        dict = {
            "Name" : self.Name,
            "UID" : self.UID,
            "Password" : self.Password,
            "ClassOf" : self.ClassOf,
            "DOB" : self.DOB,
        }
        return dict

    def __str__(self):
        return json.dumps(self.dictionary)
    
    def __repr__(self):
        return f'User(Name={self._Name}, UID={self._UID}, Password={self._Password}, ClassOf={self._ClassOf}, DOB={self._DOB})'

User1 = User(1, 1902885, 1234, 2024, 12/28/2005)
print("User1 Name ", User1.Name, "entered")
print("User1 UID ", User1.UID, "entered")
print("User1 Password ", User1.Password, "entered")
print("User1 ClassOf ", User1.Password, "entered")
print("User1 DOB ", User1.DOB, "entered")
print(User1)
User1 Name  1 entered
User1 UID  1902885 entered
User1 Password  1234 entered
User1 ClassOf  1234 entered
User1 DOB  0.00021375133594584965 entered
{"Name": 1, "UID": 1902885, "Password": 1234, "ClassOf": 2024, "DOB": 0.00021375133594584965}

Explanation

Our group is creating a web arcade/casino where you can earn tokens for the games you play and try to establish yourself on the leader board. Our product name is, "TripleAJ Arcade"

The code above provides backend development which depicts how different gamers will have different scores. The higher the score, the higher you are on the leader board. The code defined its objects, elements, and properties which is our four games and each one's score as well as the total amount of tokens which is all the points totaled.

My attributes consisted of our four games:ReactionTime, HiLo, Pong, and Snake. My last attribute was the total amount of tokens from all the four games combined.

Vocab

INT Attribute

Initialization:Sets up the initial state of the object.

Self Attribute

When a method is called an object, the first thing passed to the method is basically the object itself. Self is for referring to an object, used in many programming languages.

Setter

Sets the value of a property or a variable of an object.

Getter

Retrieves the value of a property for a given object.

Tester

Test case that is written to test specific function of a program/product, or software. It is beautiful

Tester Example Code

import unittest

class MyClass:
    def my_method(self, a, b):
        return a * b

class TestMyClass(unittest.TestCase):
    def setUp(self):
        self.my_object = MyClass()

    def test_my_method(self):
        result = self.my_object.my_method(2, 5)
        self.assertEqual(result, 10)

    def test_my_method_negative(self):
        result = self.my_object.my_method(-2, -5)
        self.assertEqual(result, 10)

if __name__ == '__main__':
    unittest.main()

PBL Hacks

from flask import Blueprint, request, jsonify
from flask_restful import Api, Resource # used for REST API building

from model.arcades import User

# Change variable name and API name and prefix
players_api = Blueprint('players_api', __name__,
                   url_prefix='/api/players')

# API docs https://flask-restful.readthedocs.io/en/latest/api.html
api = Api(players_api)

class UserAPI:        
    class _Create(Resource):
        def post(self):
            ''' Read data for json body '''
            body = request.get_json()
            
            ''' Avoid garbage in, error checking '''
            # validate name
            name = body.get('name')
            if name is None or len(name) < 2:
                return {'message': f'Name is missing, or is less than 2 characters'}, 210
            # validate uid
            uid = body.get('uid')
            if uid is None or len(uid) < 2:
                return {'message': f'User ID is missing, or is less than 2 characters'}, 210
            # look for password and dob
            password = body.get('password')
            tokens = body.get('tokens')

            ''' #1: Key code block, setup USER OBJECT '''
            uo = User(name=name, 
                      uid=uid,
                      tokens=tokens,)
            
            ''' Additional garbage error checking '''
            # set password if provided
            if password is not None:
                uo.set_password(password)            
            
            ''' #2: Key Code block to add user to database '''
            # create user in database
            player = uo.create()
            # success returns json of user
            if player:
                return jsonify(player.read())
            # failure returns error
            return {'message': f'Processed {name}, either a format error or User ID {uid} is duplicate'}, 210

    class _Read(Resource):
        def get(self):
            players = User.query.all()    # read/extract all users from database
            json_ready = [player.read() for player in players]  # prepare output in json
            return jsonify(json_ready)  # jsonify creates Flask response object, more specific to APIs than json.dumps

    class _Update(Resource):
        def update(self):
            body = request.get_json() # get the body of the request
            id = body.get('id') # get the ID (Know what to reference)

            # Set up (just in case they do not work out)
            name = ''
            uid = ''
            password = ''

            try:
                name = body.get("name") # get and update the name
            except:
                pass
            try:
                uid = body.get("uid") # get and update the uid
            except:
                pass
            try: 
                password = body.get("password") # get and update password
            except:
                pass
            
            user = User.query.get(id) # get the user (using the uid in this case)
            user.update(name, uid, password)
            return f"{user.read()} Updated"

    class _Delete(Resource):
        def delete(self):
            body = request.get_json()
            uid = body.get('uid')
            user = User.query.get(uid)
            user.delete()
            return f"{user.read()} Has been deleted"


    # building RESTapi endpoint
    api.add_resource(_Create, '/create')
    api.add_resource(_Read, '/')
    api.add_resource(_Delete, '/delete')

Python Model