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$9il..."
name: "Nicholas Tesla", id: "nick", psw: "sha256$Kxo..."
* name: "Alexander Graham Bell", id: "lex", psw: "sha256$WYB..."
name: "Eli Whitney", id: "eli", psw: "sha256$lII..."
name: "Hedy Lemarr", id: "hedy", psw: "sha256$b0T..."
Test 2, change user 3
name: "Thomas Edison", id: "toby", psw: "sha256$9il..."
name: "Nicholas Tesla", id: "nick", psw: "sha256$Kxo..."
* name: "John Mortensen", id: "jm1021", psw: "sha256$hX0..."
name: "Eli Whitney", id: "eli", psw: "sha256$lII..."
name: "Hedy Lemarr", id: "hedy", psw: "sha256$b0T..."
Test 3, make a dictionary
[{"_name": "Thomas Edison", "_uid": "toby", "_password": "sha256$9ileI9GkFO3U4krR$f839e8cedcdf970e7e6080b2f0ce8cc45d002e54056ce0b1c6366ddfb2804c4a"}, {"_name": "Nicholas Tesla", "_uid": "nick", "_password": "sha256$KxoSE02jaNyn8qEU$1734075e8f9a6fcc7c3df1ab987c11d2ccbeea4a87a0cfc886215c692a779d04"}, {"_name": "John Mortensen", "_uid": "jm1021", "_password": "sha256$hX0KUszOKQEObGeU$ff45553cf55319843e2259cbba8d6cae496e54bdce64c187493d0062645d41f4"}, {"_name": "Eli Whitney", "_uid": "eli", "_password": "sha256$lII4sLsyO9owjs5B$d5b4017a331fa50f1a5010689c51fcf6e22f9dd1a70311dd403b533724783c89"}, {"_name": "Hedy Lemarr", "_uid": "hedy", "_password": "sha256$b0TRHRCqiYeH0ptN$94e24683bf428e4f8ba9fc6dd4512caa430fc81543564956737726fd407e13a8"}]
Test 4, make a dictionary
[{"_name": "Thomas Edison", "_uid": "toby", "_password": "sha256$9ileI9GkFO3U4krR$f839e8cedcdf970e7e6080b2f0ce8cc45d002e54056ce0b1c6366ddfb2804c4a"}, {"_name": "Nicholas Tesla", "_uid": "nick", "_password": "sha256$KxoSE02jaNyn8qEU$1734075e8f9a6fcc7c3df1ab987c11d2ccbeea4a87a0cfc886215c692a779d04"}, {"_name": "John Mortensen", "_uid": "jm1021", "_password": "sha256$hX0KUszOKQEObGeU$ff45553cf55319843e2259cbba8d6cae496e54bdce64c187493d0062645d41f4"}, {"_name": "Eli Whitney", "_uid": "eli", "_password": "sha256$lII4sLsyO9owjs5B$d5b4017a331fa50f1a5010689c51fcf6e22f9dd1a70311dd403b533724783c89"}, {"_name": "Hedy Lemarr", "_uid": "hedy", "_password": "sha256$b0TRHRCqiYeH0ptN$94e24683bf428e4f8ba9fc6dd4512caa430fc81543564956737726fd407e13a8"}]

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

My Class Code

from datetime import date, timedelta 
from datetime import datetime

class Student:
    # Constructor:
    def __init__(self, current_grade, age: int):
        self.age = age
        self.date_of_birth = (date.today() - timedelta(days=(365 * age))).isoformat()
        
        current_year = datetime.now().year
        self.current_grade = current_grade
        self.graduation_year = current_year + (12 - current_grade)
    # Getters:
    def getDOB(self):
        return self.date_of_birth
    
    def getClassOf(self):
        return self.graduation_year

    def getAge(self):
        return self.age

    # Setters    
    def setDOB(self,DOB):
        self.date_of_birth = DOB

    def setClassOf(self, ClassOf):
        self.ClassOf = ClassOf

    def setAge(self, Age):
        self.Age = Age

student1 = Student(20, 25)
student2 = Student(11, 17)
print("Student 1 Date of Birth: ", student1.getDOB())
print("Student 2 Date of Birth: ", student2.getDOB())
print("Student 1 is Class of", student1.getClassOf())
print("Student 2 is Class of", student2.getClassOf())
print("Student 1 Age is", student1.getAge())
print("Student 2 Age is", student2.getAge())
Student 1 Date of Birth:  1998-01-17
Student 2 Date of Birth:  2006-01-15
Student 1 is Class of 2015
Student 2 is Class of 2024
Student 1 Age is 25
Student 2 Age is 17

Explaination

  • As you can see, I was able to add the attributes to this class function that outputs the Date of birth, grad class, and age, of an object called "student"
    • I made two different students: student1 and student2, and you can see in the output that they have different dates of birth, classes, and ages.
  • I used the init and self method to construct the grade, age, and DOB functions
  • I then used the setter and getter method to call them
  • Finally I assigned the attributes to the two students and printed them.

Design For CPT Project Application

import json

class Athlete:
    # Constructor:
    def __init__(self, Weight, Bench, Squat, Press, Pushup):
        self._Weight = Weight
        self._Bench = Weight + (50)
        self._Squat = Weight *(2)
        self._Press = Weight /(2)
        self._Pushup = Bench /(5)
    
    # Getters:
    @property
    def Weight(self):
        return self._Weight
    
    @property
    def Bench(self):
        return self._Bench

    @property
    def Squat(self):
        return self._Squat   

    @property
    def Press(self):
        return self._Press 
    
    @property
    def Pushup(self):
        return self._Pushup 
   
    # Setters    
    @Weight.setter
    def Weight(self,weight):
        self._Weight = weight

    @Bench.setter
    def Bench(self, bench):
        self._Bench = bench

    @Squat.setter
    def Squat(self, squat):
        self._Squat = squat

    @Press.setter
    def Press(self, press):
        self._Press = press

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

    @property
    def dictionary(self):
        dict = {
            "Weight" : self.Weight,
            "Bench" : self.Bench,
            "Squat" : self.Squat,
            "Press" : self.Press,
            "Pushup" : self.Pushup,
        }
        return dict

    def __str__(self):
        return json.dumps(self.dictionary)
    
    def __repr__(self):
        return f'Athlete(Weight={self._Weight}, Bench={self._Bench}, Squat={self._Squat}, Press={self._Press}, Pushup={self._Pushup})'

Athlete1 = Athlete(130, 180, 260, 65, 36)
Athlete2 = Athlete(190, 240, 380, 95, 48)
print("Athlete 1 Weighs ", Athlete1.Weight, "lbs")
print("Athlete 1 Bench Presses ", Athlete1.Bench, "lbs")
print("Athlete 1 Squats ", Athlete1.Squat, "lbs")
print("Athlete 1 Presses ", Athlete1.Press, "lbs")
print("Athlete 1 Can do ", Athlete1.Pushup, "push-ups")
print("Athlete 2 Weighs ", Athlete2.Weight, "lbs")
print("Athlete 2 Bench Presses ", Athlete2.Bench, "lbs")
print("Athlete 2 Squats ", Athlete2.Squat, "lbs")
print("Athlete 2 Presses ", Athlete2.Press, "lbs")
print("Athlete 2 Can do ", Athlete2.Pushup, "push-ups")
print(Athlete1)
print(Athlete2)
Athlete 1 Weighs  130 lbs
Athlete 1 Bench Presses  180 lbs
Athlete 1 Squats  260 lbs
Athlete 1 Presses  65.0 lbs
Athlete 1 Can do  36.0 push-ups
Athlete 2 Weighs  190 lbs
Athlete 2 Bench Presses  240 lbs
Athlete 2 Squats  380 lbs
Athlete 2 Presses  95.0 lbs
Athlete 2 Can do  48.0 push-ups
{"Weight": 130, "Bench": 180, "Squat": 260, "Press": 65.0, "Pushup": 36.0}
{"Weight": 190, "Bench": 240, "Squat": 380, "Press": 95.0, "Pushup": 48.0}

Explaination

  • As you can see, I made a function that is very similar to the class function from before, except it tailors to my groups CPT project.
    • My group is doing a fitness tracker webapp for our CPT, so this function is based on an athlete
  • I began with constructing the Weight and Bench attributes that signify how much an athlete weighs and bench presses
  • I then used the Setter and Getter method to call them
  • Finally I assigned each attribute to Athlete1 and Athlete2 and printed them.

Later Edits

  • In later edits, Steven and I were able to add @ decorators, and use the def__str function to print athlete 1 and athlete 2 and their attributes.

Init attribute

  • "init" is short for initialization
    • the process of setting up the environment or initial state of a program or system before it begins to execute.
    • In object-oriented programming, it often refers to the constructor method of a class, which is called when a new instance of the class is created, and is responsible for setting up the initial state of the object.
    • In other contexts, an "init" function or script is used to set up a specific environment or configuration for a program to run.

Self Attribute

  • When a method is called on an object, the first argument passed to the method is the object itself.
    • The convention in Python (and many other programming languages) is to use the name self to refer to this object

Setter

  • a method that is used to set the value of a property or variable of an object.
    • It's often used in conjunction with a "getter" method, which is used to retrieve the value of the property or variable.
    • The setter method typically takes a single argument, which is the new value to be assigned to the property or variable.
      • may also include additional logic, such as validation or other processing that should be performed when the value of the property or variable is changed

Getter

  • a method that is used to retrieve the value of a property or variable of an object.
    • It's often used in conjunction with a "setter" method, which is used to set the value of the property or variable.
    • typically takes no arguments and returns the current value of the property or variable.
    • It's often used to provide read access to the internal state of an object, while keeping the internal state hidden from the outside world
    • Getters and Setters are also called accessors and mutators respectively.

Tester

  • A tester method in programming is a specific test case that is written to test a specific method or function of a software.
  • It is used to check that a specific functionality of the software behaves as expected
    • and that it meets the requirements and specifications defined in the design phase.

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()
  • In this example, MyClass is a simple class with a single method my_method(a,b) that takes two numbers as input and returns their product.
  • TestMyClass is a test case class provided by unittest library and it has 2 test functions test_my_method and test_my_method_negative.
  • setUp() function is called before each test function, it create an instance of MyClass so that we can use it in test functions.
  • test_my_method() tests the my_method() function of the MyClass class with the input 2,5, it is expected to get the output of 10, so the assertion assertEqual(result, 10) is comparing the result with the expected output.
  • test_my_method_negative() is testing the same method with the negative inputs and it is also expecting the result to be 10 by using the same assertion.