<jupyter_start><jupyter_text>Python OOP Tutorial 3 - Classmethods and Staticmethods[(video link)](https://youtu.be/rq8cL2XMM5M) | [(original code)](https://github.com/CoreyMSchafer/code_snippets/tree/master/Object-Oriented/3-Class-Static-Methods) | [(transcript)](https://github.com/faizankshaikh/Notes_PythonOOPTutorial/blob/master/transcripts/classmethods_and_staticmethods.txt)---  Table of Contents 3.1 What is classmethod? 3.2 Use case - Set employees raise amount using classmethod 3.3 Using classmethods as alternative constructors 3.4 Real world example of classmethod 3.5 What is staticmethod? 3.6 Use case - Whether or not a date is a work day---  3.1 What is classmethod? Instead of a method taking self (which is an instance) as the first argument, how can we change this so that the method instead takes class as the first argument? We use classmethod for this.To change a regular method to class method, add a decorator to the top called @classmethod  3.2 Use case - Set employees raise amount using classmethod<jupyter_code>class Employee:

    num_of_emps = 0
    raise_amount = 1.04

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

        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)

    # create classmethod set_raise_amount using decorator @classmethod
    @classmethod
    def set_raise_amount(cls, amount):
        cls.raise_amount = amount


emp_1 = Employee("Corey", "Schafer", 50000)
emp_2 = Employee("Test", "User", 60000)

# print before calling class method set_raise_amount
print(Employee.raise_amount)
print(emp_1.raise_amount)
print(emp_2.raise_amount)
# call the classmethod set_raise_amount
Employee.set_raise_amount(1.05)

# print after calling class method set_raise_amount
print(Employee.raise_amount)
print(emp_1.raise_amount)
print(emp_2.raise_amount)<jupyter_output>1.05
1.05
1.05<jupyter_text>*Note -**1. To learn more about how decorators work, watch [this video](https://youtu.be/FsAPt_9Bf3U). Basically, it alters the functionality of our method which now recieved class as first argument instead of the instance**2. By convention, we call this class as "cls". We can't use "class" here because it has a different meaning in Python (it is a keyword in python), i.e. it is used to create a new class**3. Calling Employee.set_raise_amount(1.05) is same as doing Employee.raise_amount = 1.05. We can also do emp_1.set_raise_amount(1.05), but that doesnt make a lot of sense and no one ever uses it*  3.3 Using classmethods as alternative constructors We can also use class methods as alternative constructors, i.e. we can use the class methods in order to provide multiple ways of creating our objects. For example, if we have a specific usecase where we are getting employee information in the form of a string, and we have to create an employee from that<jupyter_code>emp_str_1 = "John-Doe-70000"
emp_str_2 = "Steve-Smith-30000"
emp_str_3 = "Jane-Doe-90000"

# split on hyphen
first, last, pay = emp_str_1.split("-")

new_emp_1 = Employee(first, last, pay)

print(new_emp_1.email)
print(new_emp_1.pay)<jupyter_output><EMAIL>
70000<jupyter_text>If this is a common use case, we don't want to parse these strings evey time they want to create a new employee. So let's create an alternative constructor that allows to pass in the string and we can create employee from that<jupyter_code>class Employee:

    num_of_emps = 0
    raise_amount = 1.04

    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + "." + last + <EMAIL>"

        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

    # create alternative constructor
    @classmethod
    def from_string(cls, emp_str):
        # use emp_str argument that we passed in the classmethod
        first, last, pay = emp_str.split("-")
        # use cls for accessing the class and return the instance created
        return cls(first, last, pay)


emp_str_1 = "John-Doe-70000"

new_emp_1 = Employee.from_string(emp_str_1)

print(new_emp_1.email)
print(new_emp_1.pay)<jupyter_output><EMAIL>
70000<jupyter_text>*Note - By convention, alternative constructors start with "from"*  3.4 Real world example of classmethod A real world example is the datetime module (as we can see below)![](../images/datetime_classmethods.png)  3.5 What is staticmethod? When working with classes, * Regular methods automatically pass the instance as the first argument (we call that self)* Class methods automatically pass the class as the first argument (we call that cls)* **Static methods dont pass in anything automatically**, i.e. they dont pass the instance or the class. So they behave like a regular function except that **we include them in our classes because they have some logical connection with the class**  3.6 Use case - Whether or not a date is a work day Let's say that we wanted a simple function that would take in a date and return whether or not that was a workday. So this has a logical connection to our employee class but it doesn't actually depend on any specific instance or class variable<jupyter_code>class Employee:

    num_of_emps = 0
    raise_amount = 1.04

    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + "." + last + <EMAIL>"

        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):
        first, last, pay = emp_str.split("-")
        return cls(first, last, pay)

    # create staticmethod is_workday
    # note that we dont have to pass in class or instance
    @staticmethod
    def is_workday(day):
        if day.weekday() == 5 or day.weekday() == 6:
            return False
        return True


emp_1 = Employee("Corey", "Schafer", 50000)
emp_2 = Employee("Test", "User", 60000)

# check if 10th is a weekday
import datetime

my_date = datetime.date(2016, 7, 10)

print(Employee.is_workday(my_date))<jupyter_output>False<jupyter_text>This prints False because its actually a Sunday<jupyter_code># instead of 10th, we check for 11th
my_date = datetime.date(2016, 7, 11)

print(Employee.is_workday(my_date))<jupyter_output>True
StarcoderdataJupyterStructuredCleanDedup