Python OOP

Python OOP Classes and Instances:

class Employee:

    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

    def fullname(self):
        return '{} {}'.format(self.first, self.last)

emp_1 = Employee('Alex', 'Brown', 50000)
emp_2 = Employee('Lisa', 'Miller', 60000)

print(type(emp_1)) # <class '__main__.Employee'>

print(emp_1.email) # Alex.Brown@email.com

print(emp_2.fullname()) # Lisa Miller
print(Employee.fullname(emp_2)) # Lisa Miller

Python OOP Class Variables:

Class Variables shared for all instances of the class.

class Employee:
    raise_amount = 1.04
    num_of_emps = 0
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

        Employee.num_of_emps += 1

    def fullname(self):
        return '{} {}'.format(self.first, self.last)
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)
        
emp_1 = Employee('Alex', 'Brown', 50000)
emp_2 = Employee('Lisa', 'Miller', 60000)

print(emp_1.raise_amount) # 1.04
print(Employee.raise_amount) # 1.04
print(emp_1.pay) # 50000
emp_1.apply_raise()
print(emp_1.pay) # 52000

Employee.raise_amount = 1.05

print(Employee.raise_amount) # 1.05
print(emp_2.raise_amount) # 1.05

emp_2.raise_amount = 1.07

print(emp_2.raise_amount) # 1.07

print(emp_1.__dict__)
# {'first': 'Alex', 'last': 'Brown', 'email': 'Alex.Brown@email.com', 'pay': 50000}

print(emp_2.__dict__)
# {'first': 'Lisa', 'last': 'Miller', 'email': 'Lisa.Miller@email.com', 'pay': 60000, 'raise_amount': 1.07}

print(Employee.__dict__)
# {'__module__': '__main__', 'raise_amount': 1.04, 'num_of_emps': 2,
# '__init__': <function Employee.__init__ at 0x000001F34784EF28>,
# 'fullname': <function Employee.fullname at 0x000001F347852048>,
# 'apply_raise': <function Employee.apply_raise at 0x000001F3478520D0>,
# '__dict__': <attribute '__dict__' of 'Employee' objects>,
# '__weakref__': <attribute '__weakref__' of 'Employee' objects>, '__doc__': None}

print(Employee.num_of_emps) # 2

Python OOP classmethods

class Employee:
    raise_amount = 1.04
    num_of_emps = 0
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

        Employee.num_of_emps += 1

    def fullname(self):
        return '{} {}'.format(self.first, self.last)
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)

    @classmethod
    def set_raise_amount(cls, amount):
        cls.raise_amount = amount

    @classmethod
    def from_string(cls, emp_str): # Alternative constructor
        first, last, pay = emp_str.split('-')
        return cls(first, last, pay)
        
emp_1 = Employee('Alex', 'Brown', 50000)
emp_2 = Employee('Lisa', 'Miller', 60000)


print(Employee.raise_amount) # 1.04
print(emp_1.raise_amount) # 1.04
print(emp_2.raise_amount) # 1.04

Employee.set_raise_amount(1.05)
print(Employee.raise_amount) # 1.05
print(emp_1.raise_amount) # 1.05
print(emp_2.raise_amount) # 1.05

emp_3 = Employee.from_string('Bob-Fox-70000')
print(emp_3.fullname()) # Bob Fox

Python OOP staticmethods

class Employee:
    raise_amount = 1.04
    num_of_emps = 0
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

        Employee.num_of_emps += 1

    def fullname(self):
        return '{} {}'.format(self.first, self.last)
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)

    @classmethod
    def set_raise_amount(cls, amount):
        cls.raise_amount = amount

    @classmethod
    def from_string(cls, emp_str): # Alternative constructor
        first, last, pay = emp_str.split('-')
        return cls(first, last, pay)

    @staticmethod
    def is_workday(day):
        if day.weekday() == 5 or day.weekday() == 6: # Saturday or Sunday
            return False
        return True
        
emp_1 = Employee('Alex', 'Brown', 50000)
emp_2 = Employee('Lisa', 'Miller', 60000)


import datetime

my_date = datetime.date(2015,4,19)
print(Employee.is_workday(my_date)) # False

Python OOP Inheritance - Creating Subclasses

class Employee:
    raise_amount = 1.04
    num_of_emps = 0
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

        Employee.num_of_emps += 1

    def fullname(self):
        return '{} {}'.format(self.first, self.last)
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)

    @classmethod
    def set_raise_amount(cls, amount):
        cls.raise_amount = amount

    @classmethod
    def from_string(cls, emp_str): # Alternative constructor
        first, last, pay = emp_str.split('-')
        return cls(first, last, pay)

    @staticmethod
    def is_workday(day):
        if day.weekday() == 5 or day.weekday() == 6: # Saturday or Sunday
            return False
        return True

class Developer(Employee):
    raise_amount = 1.10

    def __init__(self, first, last, pay, prog_lang):
        super().__init__(first, last, pay)
        # Employee.__init__(self, first, last, pay) # Same as before
        self.prog_lang = prog_lang

class Manager(Employee):

    def __init__(self, first, last, pay, employees=None):
        super().__init__(first, last, pay)
        if employees is None:
            self.employees = []
        else:
            self.employees = employees

    def add_emp(self, emp):
        if emp not in self.employees:
            self.employees.append(emp)

    def del_emp(self, emp):
        if emp in self.employees:
            self.employees.remove(emp)

    def print_emps(self):
        for emp in self.employees:
            print('=emp=', emp.fullname())


dev_1 = Developer('Alex', 'Brown', 50000, 'Python')
dev_2 = Developer('Lisa', 'Miller', 60000, 'JavaScript')

print(type(dev_1)) # <class '__main__.Developer'>
# print(help(Developer)) # Shows info on Developer class

print(dev_1.fullname()) # Alex Brown
print(dev_2.fullname()) # Lisa Miller

print(dev_1.prog_lang) # Python
print(dev_2.prog_lang) # JavaScript

print(dev_1.pay) # 50000
dev_1.apply_raise()
print(dev_1.pay) # 55000

mgr_1 = Manager('Jack', 'Russell', 70000)
mgr_1.add_emp(dev_1)
mgr_1.print_emps() # Alex Brown
mgr_1.add_emp(dev_2)
mgr_1.print_emps() # Alex Brown, Lisa Miller

print(isinstance(mgr_1, Manager)) # True
print(isinstance(mgr_1, Employee)) # True

print(isinstance(dev_1, Manager)) # False

print(issubclass(Manager, Employee)) # True
print(issubclass(Manager, Developer)) # False

Python OOP Special (Magic/Dunder) Methods

More info on Special (Magic/Dunder) Methods
Special method names.

class Employee:
    raise_amount = 1.04
    num_of_emps = 0
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'
        self.pay = pay

        Employee.num_of_emps += 1

    def fullname(self):
        return '{} {}'.format(self.first, self.last)
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)

    # This method is used for debug. It makes print(cls) and repr(cls) look pretty
    def __repr__(self):
        return "Employee('{}', '{}', {})".format(self.first, self.last, self.pay)

    # This method is used for debug. It makes print(cls) and str(cls) look pretty
    def __str__(self):
        return '{} - {}'.format(self.fullname(), self.email)

    def __len__(self):
        return len(self.fullname())
        
emp_1 = Employee('Alex', 'Brown', 50000)
emp_2 = Employee('Lisa', 'Miller', 60000)

print(emp_1) # Alex Brown - Alex.Brown@email.com

print(repr(emp_1)) # Employee('Alex', 'Brown', 50000)
print(emp_1.__repr__()) # same as prev line
print(str(emp_1)) # Alex Brown - Alex.Brown@email.com
print(emp_1.__str__()) # same as prev line

print(len(emp_1)) # 10

Python OOP Property Decorators - Getters, Setters, and Deleters

class Employee:
    
    def __init__(self, first, last):
        self.first = first
        self.last = last

    @property
    def email(self):
        return '{}.{}@email.com'.format(self.first, self.last)

    @property
    def fullname(self):
        return '{} {}'.format(self.first, self.last)

    @fullname.setter
    def fullname(self, name):
        self.first, self.last = name.split(' ')
        
    @fullname.deleter
    def fullname(self):
        print('Deleted!')
        self.first = None
        self.last = None
        
emp_1 = Employee('Alex', 'Brown')
emp_2 = Employee('Lisa', 'Miller')

emp_1.first = 'Jim'
print(emp_1.fullname) # Jim Brown
print(emp_1.email) # Jim.Brown@email.com


emp_1.fullname = 'Jack Russell'

print(emp_1.first) # Jack
print(emp_1.fullname) # Jack Russell
print(emp_1.email) # Jack.Russell@email.com

del emp_1.fullname

print(emp_1.first) # None
print(emp_1.fullname) # None None

Source: CoreyMS.com