Terms

  • Class
    • 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.
  • Object is a instance of a 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
      • 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)
      • enables developers to reference/get instance data in a shorthand fashion (object.name versus object.get_name())
    • @name.setter decorator (aka setter)
      • 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 ""
      • allows setters and getters to work with more natural variable name (name, email ...)
# 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
from datetime import date

# 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, classOf, dob):
        self._name = name    # variables with self prefix become part of the object, 
        self._uid = uid
        self.set_password(password)
        self.classOf=classOf
        self.dob=dob
        self.age=self.calculate_age()

    # 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
    
    # getter method for classOf
    def get_classOf(self):
        return self.classOf
    
    # setter method for classOf
    def set_classOf(self,classOf):
        self.classOf = classOf
    
    #getter method for DoB
    def get_dob(self):
        return self.dob
    
    # setter method for DoB and changing age
    def set_dob(self,dob):
        self.dob = dob
        self.age=self.calculate_age()
    
    def calculate_age(self):
        today = date.today()
        return today.year - self.dob.year - ((today.month, today.day) < (self.dob.month, self.dob.day))
    
    # getter method for age
    def get_age(self):
        return self.age
    
    # 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}", classOf: "{self.classOf}", dob: "{self.dob}", age: "{self.age}"'

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


# 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__":
    u = User(name='Alexander Graham Bell', uid='lex', password='123lex', classOf=1858, dob=date(1847,3,3))


    # Find user
    print("print our user")
    print(u)


    # Change user
    print("changing our user")
    u.name = "John Mortensen"
    u.uid = "jm1021"
    u.set_password("123qwerty")
    u.set_classOf(1991)
    u.set_dob(date(1959,4,5)) # I have no idea lmao
    print(u)
print our user
name: "Alexander Graham Bell", id: "lex", psw: "sha256$0xc...", classOf: "1858", dob: "1847-03-03", age: "175"
changing our user
name: "John Mortensen", id: "jm1021", psw: "sha256$eTM...", classOf: "1991", dob: "1959-04-05", age: "63"

Self-Created Class

Here is a class for fractions that I created, with functions for printing and getting variables and changing the built-in funtions for arithmetic methods

from math import *
class Fraction:
    """represents fractions"""

    def __init__(self, num, denom):
        """Fraction(num,denom) -> Fraction
        creates the fraction object representing num/denom"""
        if denom == 0:  # raise an error if the denominator is zero
            raise ZeroDivisionError
        if num == 0: # set the fraction to 0/1 if numerator is 0
            self.num = 0
            self.denom = 1
            return
        positive = True # keeps track of positivity
        if denom < 0: # if denominator is negative, change it to positive and keeps track
            positive = not positive
            denom = -1 * denom
        if num < 0: # do the same to numerator
            positive = not positive
            num = -1 * num
        self.num = int(num / gcd(num, denom)) # the gcd only works when both is positive
        self.denom = int(denom / gcd(num, denom))
        if not positive: # change numerator to negative if positive is false
            self.num = -1 * self.num
    
    def set_numerator(self, num):
        """Fraction.set_numerator(num) -> None
        changes the numerator of the fraction"""
        self.__init__(num, self.denom)
    
    def get_numerator(self):
        """Fraction.get_numerator() -> int
        returns the numerator of the fraction"""
        return self.num
    
    def set_denominator(self, denom):
        """Fraction.denominator(denom) -> None
        changes the denominator of the fraction"""
        self.__init__(self.num, denom)
    
    def get_denominator(self):
        """Fraction.get_denominator() -> int
        returns the denominators of the fraction"""
        return self.denom
    
    def __str__(self):
        """Fraction.__str__() -> string
        returns a string representing the fraction"""
        return f"{self.num}/{self.denom}"

    def __float__(self):
        """Fraction.__float__() -> float
        returns a float representing the value of the fraction"""
        return self.num / self.denom

    def __add__(self, other):
        """Fraction+Fraction -> Fraction
        returns a Fraction that is the results of adding two together"""
        denomFinal = int(lcm(self.denom, other.denom))
        numFinal = int(self.num * denomFinal / self.denom + other.num * denomFinal / other.denom)
        return Fraction(numFinal, denomFinal)

    def __sub__(self, other):
        """Fraction-Fraction -> Fraction
        returns a Fraction that is the results of subtraction one fraction from another"""
        denomFinal = int(lcm(self.denom, other.denom))
        numFinal = int(self.num * denomFinal / self.denom - other.num * denomFinal / other.denom)
        return Fraction(numFinal, denomFinal)

    def __mul__(self, other):
        """Fraction*Fraction -> Fraction
        returns a Fraction that is the results of multiplying two together"""
        return Fraction(self.num * other.num, self.denom * other.denom)

    def __truediv__(self, other):
        """Fraction/Fraction -> Fraction
        returns a Fraction that is the results of dividing one fraction by another"""
        return Fraction(self.num * other.denom, self.denom * other.num)

    def __eq__(self, other):
        """Fraction==Fraction -> boolean
        returns a boolean saying if two fraction is equal to each other"""
        if self.num == other.num and self.denom == other.denom:
            return True
        else:
            return False

a=Fraction(1,2)
b=Fraction(2,3)
print(a,b)
print(a+b)
print(b-a)
print(a*b)
print(b/a)
print(float(a))
print(a.get_numerator())
print(b.get_denominator())
a.set_denominator(4)
b.set_numerator(1)
print(a,b)
print(a==b)
1/2 2/3
7/6
1/6
1/3
4/3
0.5
1
3
1/4 1/3
False