Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 9

Class Components: Methods and Properties

Recommended resources

Hello! Welcome to the third lesson in our module on Object-Oriented Programming in Python.

In our last lesson, we explored inheritance and saw how a child class can inherit and override methods from a parent class. We focused on methods that are tied to a specific object or instance, which always take self as their first argument.

Today, we're going to expand our toolkit. This lesson addresses the learning outcome: Use class methods, static methods, and properties. We'll cover:

  • Class Methods: Methods that operate on the class itself, not the instance.
  • Static Methods: Functions that are grouped within a class for logical reasons but don't depend on the instance or the class.
  • Properties: A Pythonic way to manage attribute access, allowing you to create "getters" and "setters" without changing your class's public interface.

Understanding these concepts is key to designing flexible and robust classes, a skill that's invaluable when working with complex machine learning frameworks.

Regular Instance Methods: A Quick Recap

So far, every method we've written has been an instance method.

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

    # This is an instance method
    def fullname(self):
        return f'{self.first} {self.last}'

The fullname method needs a specific instance (via self) to do its job. You can't call Employee.fullname() because it wouldn't know which employee's name to return.

But what if a method's logic is related to the class as a whole?


Class Methods: Working with the Class

A class method is bound to the class, not the object. Instead of the instance (self), it implicitly receives the class itself as the first argument, conventionally named cls.

You create one using the @classmethod decorator.

Types of Methods in Python Classes
This image shows the basic syntax for instance, class, and static methods. Notice the first argument for each: `self` for instance methods, `cls` for class methods, and none for static methods.

Class methods have two primary use cases:

  1. Modifying class-level state.
  2. Serving as alternative constructors (factory methods).

Let's see this in action. The following video continues with the Employee class example from our previous lessons.

Python OOP Tutorial 3: classmethods and staticmethods

This segment from Corey Schafer's tutorial explains what class methods are, how to create them with the @classmethod decorator, and demonstrates their two main use cases.

Watch from 00:20 to 10:21. Pay close attention to: How @classmethod changes the first argument from self to cls. How a class method can be used to modify a class variable (like raise_amount). The concept of using class methods as 'alternative constructors', like creating an employee from a string.

As the video showed, using a class method as an alternative constructor is an extremely powerful pattern. Imagine you need to create objects from various data sources like a string, a dictionary from a JSON file, or a row from a database. Instead of cluttering your __init__ with complex logic, you can provide clean, separate class methods for each case.

class Employee:
    # ... __init__ and other methods ...

    @classmethod
    def from_string(cls, emp_str):
        # Parses a string like "John-Doe-70000"
        first, last, pay = emp_str.split('-')
        # 'cls' here refers to the Employee class
        return cls(first, last, int(pay)) 

# Now we can create an instance directly from a string
new_emp = Employee.from_string('Jane-Doe-90000')
print(new_emp.email) # Output: Jane.Doe@company.com

Static Methods: Logically Grouped Functions

What if a method doesn't need access to the instance (self) or the class (cls)? That's when you use a static method.

A static method is essentially a regular function that lives inside a class's namespace, usually because it's logically related to the class. You create one using the @staticmethod decorator.

Let's continue with the video to see a practical example.

Python OOP Tutorial 3: classmethods and staticmethods

This final part of the video explains static methods, showing how they behave like regular functions but are organized within the class.

Watch from 10:21 to 14:29. Focus on why the is_workday function makes sense as a static method: it's related to employees, but it doesn't need any specific employee's data or class data to work.

A good rule of thumb for deciding between method types is:

  • Does it need the instance's data (self)? Use an instance method. (Most common)
  • Does it need the class's data (cls), like for an alternative constructor? Use a class method.
  • Does it need neither? Use a static method for organizational purposes.
Python Classmethod vs Staticmethod Comparison
This infographic provides a side-by-side comparison, highlighting the key differences between class methods and static methods regarding their decorators, first argument, and access to class state.
Test your understanding!

You are building a Car class. For each of the following functions, decide whether it should be an instance method, a class method, or a static method.

  1. A function description() that returns a string like "A 2023 Toyota Camry".
  2. A function from_database_record() that takes a dictionary (e.g., {'make': 'Ford', 'model': 'Mustang', 'year': 2022}) and creates a Car object.
  3. A function is_valid_vin() that takes a Vehicle Identification Number (string) and checks if it follows a standard format (e.g., is 17 characters long).
Show answer
  1. Instance method. It needs access to the specific car's year, make, and model attributes (i.e., self).
  2. Class method. It's an alternative way to construct a Car object. It needs access to the class (cls) to call cls(make, model, year).
  3. Static method. It performs a validation check that is logically related to cars, but it doesn't need information about a specific car instance or the Car class itself. It just operates on the input it's given.

Properties: Accessing Methods Like Attributes

Let's revisit our Employee class. We have a problem with dependent attributes.

emp_1 = Employee('John', 'Smith', 50000)
print(emp_1.first)    # John
print(emp_1.email)    # John.Smith@company.com

# Let's change the first name
emp_1.first = 'Jim'

print(emp_1.first)    # Jim
print(emp_1.email)    # John.Smith@company.com  <- PROBLEM! The email did not update.

The email was created once in __init__ and is now out of sync. We could turn email into a method (emp_1.email()), but that would force anyone using our class to change their code from emp_1.email to emp_1.email(). This is not ideal.

The @property decorator provides a solution. It lets us "wrap" a method so that it can be accessed like an attribute. This is often called a "getter".

Python OOP Tutorial 6: Property Decorators - Getters, Setters, and Deleters

This excellent video from Corey Schafer introduces the @property decorator to solve the exact problem we just described. It shows how to create getters, setters, and deleters.

Watch from the beginning to 08:08. Pay attention to: How @property allows you to access a method as an attribute (the 'getter'). The syntax for creating a 'setter' (@property_name.setter) to allow modification. The syntax for the 'deleter' (@property_name.deleter).

Let's summarize the three parts of a property:

  1. Getter (@property): Makes a method accessible as an attribute. It computes and returns a value on the fly.

    @property
    def email(self):
        return f'{self.first}.{self.last}@email.com'
    
    # Now emp_1.email always returns the up-to-date email
    
  2. Setter (@<property_name>.setter): Allows you to assign a value to the property, running custom code in the process.

    @fullname.setter
    def fullname(self, name):
        first, last = name.split(' ')
        self.first = first
        self.last = last
    
    # emp_1.fullname = 'Corey Schafer' now works and updates self.first and self.last
    
  3. Deleter (@<property_name>.deleter): Defines custom behavior for when the attribute is deleted with del.

    @fullname.deleter
    def fullname(self):
        print('Deleting name!')
        self.first = None
        self.last = None
    

For a deeper dive and more examples of when to use each of these decorators, the following article is an excellent resource.

Keys to Mastering Python Method Decorators

The article 'Keys to Mastering Python Method Decorators' by Heval Hazal Kurt provides a thorough explanation with clear use cases for @classmethod, @staticmethod, and @property.

Skim through the sections on @classmethod, @staticmethod, and @property. Focus on the 'When to Use' and 'Use Cases' subsections for each to solidify your understanding of their practical applications. Don't worry about the more advanced metaprogramming examples for now.


Conclusion

In this lesson, you've significantly upgraded your OOP toolkit. You now have fine-grained control over how methods and attributes behave in your classes.

Key Takeaways:

  • Instance Methods operate on an object's state (self).
  • @classmethod methods operate on the class itself (cls) and are ideal for alternative constructors or modifying class-level variables.
  • @staticmethod methods are utility functions that don't depend on instance or class state but belong logically within the class.
  • @property allows you to create getters, setters, and deleters, enabling computed attributes and controlled access while maintaining a clean, attribute-like interface.

Preview of the Next Lesson:
Properties are a Pythonic way to manage access to an object's internal data. This is a core part of a broader OOP principle called encapsulation. In our next lesson, "Apply encapsulation principles using private and protected attributes," we will explore how Python signals that certain attributes are for internal use and should not be modified directly from outside the class.