0%

37- Methods in OOP

Instance methods, class methods, static methods, and property methods. Master the different types of methods and when to use each one.

In procedural programming, functions stand alone. In object-oriented programming, methods belong to classes. But not all methods are the same. Python gives you four distinct types of methods, each with its own purpose and behavior. Instance methods are the most common. They operate on individual objects. Class methods operate on the class itself. Static methods are like regular functions that live inside a class namespace. Property methods allow you to define attributes that behave like methods. Choosing the right type of method makes your code clearer, more efficient, and more intuitive. This lesson explains each type in depth, with examples and guidelines for when to use each one.

🕯️ Magic Note

The distinction between these method types is one of the unique features of Python. Unlike Java or C++ where everything is an instance method unless declared static, Python gives you explicit tools. The decorators @classmethod, @staticmethod, and @property make your intentions clear to anyone reading your code.

Instance Methods (The Most Common)
Instance methods are functions defined inside a class that take self as the first parameter. They operate on a specific instance of the class. They can access and modify instance attributes and can also access class attributes.

Python

class Dog:

species = “Canis familiaris” # Class attribute

def __init__(self, name, age):

self.name = name

self.age = age

# Instance method

def bark(self):

print(f”{self.name} says: Woof!”)

# Instance method that modifies instance attribute

def celebrate_birthday(self):

self.age += 1

print(f”{self.name} is now {self.age} years old!”)

# Instance method that accesses class attribute

def get_species(self):

return self.species # Works because instance can access class attribute

buddy = Dog(“Buddy”, 3)

buddy.bark() # Buddy says: Woof!

buddy.celebrate_birthday() # Buddy is now 4 years old!

print(buddy.get_species()) # Canis familiaris

💡 Instance methods are the default. When you define a function inside a class with self as the first parameter, it is an instance method. Use them for operations that need to access or modify instance-specific data.
Class Methods (@classmethod)
Class methods operate on the class itself rather than on instances. They take cls as the first parameter (instead of self). Use the @classmethod decorator. They can modify class attributes and serve as alternative constructors.

Python

class Dog:

total_dogs = 0

def __init__(self, name, age):

self.name = name

self.age = age

Dog.total_dogs += 1

# Class method

@classmethod

def get_total_dogs(cls):

return f”Total dogs created: {cls.total_dogs}”

# Class method as alternative constructor

@classmethod

def from_birth_year(cls, name, birth_year, current_year=2025):

age = current_year – birth_year

return cls(name, age) # Creates and returns a new Dog instance

# Class method to modify class attribute

@classmethod

def reset_count(cls):

cls.total_dogs = 0

return “Dog count reset”

# Called on the class (not on an instance)

print(Dog.get_total_dogs()) # Total dogs created: 0

buddy = Dog(“Buddy”, 3)

max = Dog(“Max”, 5)

print(Dog.get_total_dogs()) # Total dogs created: 2

# Alternative constructor

rocky = Dog.from_birth_year(“Rocky”, 2020)

print(rocky.name, rocky.age) # Rocky 5

print(Dog.get_total_dogs()) # Total dogs created: 3

# Can also be called on an instance (but the instance is ignored)

print(buddy.get_total_dogs()) # Total dogs created: 3 (works but not recommended)

🕯️ Magic Note

Class methods are often used for factory methods (alternative constructors). The built-in datetime.now(), datetime.fromtimestamp(), and datetime.fromisoformat() are class methods of the datetime class.

Static Methods (@staticmethod)
Static methods do not receive self or cls. They are just regular functions that live inside a class namespace. Use the @staticmethod decorator. They cannot access instance or class attributes directly (unless passed as arguments).

Python

class MathOperations:

@staticmethod

def add(a, b):

return a + b

@staticmethod

def is_even(number):

return number % 2 == 0

@staticmethod

def factorial(n):

if n <= 1:

return 1

return n * MathOperations.factorial(n – 1)

# Called on the class

print(MathOperations.add(5, 3)) # 8

print(MathOperations.is_even(10)) # True

print(MathOperations.factorial(5)) # 120

# Static methods in a real-world class

class Validator:

@staticmethod

def is_valid_email(email):

return “@” in email and “.” in email.split(“@”)[-1]

@staticmethod

def is_strong_password(password):

return len(password) >= 8 and any(c.isdigit() for c in password)

print(Validator.is_valid_email(“test@example.com”)) # True

print(Validator.is_strong_password(“weak”)) # False

💡 Use static methods when the function logic does not depend on instance or class state. They are useful for utility functions, validations, and helper functions that are conceptually related to the class.
Property Methods (@property)
Property methods allow you to define methods that can be accessed like attributes. They are useful for computed attributes, validation, and read-only attributes.

Python

class Circle:

def __init__(self, radius):

self._radius = radius # Internal attribute (convention)

# Getter property

@property

def radius(self):

return self._radius

# Setter property

@radius.setter

def radius(self, value):

if value < 0:

raise ValueError(“Radius cannot be negative”)

self._radius = value

# Computed property (read-only)

@property

def area(self):

return 3.14159 * self._radius ** 2

# Computed property (read-only)

@property

def diameter(self):

return self._radius * 2

c = Circle(5)

print(c.radius) # 5 (accessed like an attribute, not a method)

print(c.area) # 78.53975 (computed automatically)

print(c.diameter) # 10

c.radius = 10 # Uses the setter

print(c.area) # 314.159

# c.radius = -5 # ValueError: Radius cannot be negative

# c.area = 100 # AttributeError: can’t set attribute (read-only)

🕯️ Magic Note

Properties allow you to add logic to attribute access without changing the interface. You can start with a simple attribute and later replace it with a property without breaking code that uses the class. This is called design by contract and is a key principle of encapsulation.

Real-World Example: Temperature Class
A complete example demonstrating multiple method types working together.

Python

class Temperature:

# Class constant

ABSOLUTE_ZERO_C = -273.15

def __init__(self, celsius=0):

self._celsius = celsius # Internal storage in Celsius

# Instance method (getter property)

@property

def celsius(self):

return self._celsius

@celsius.setter

def celsius(self, value):

if value < Temperature.ABSOLUTE_ZERO_C:

raise ValueError(f”Temperature cannot be below absolute zero ({Temperature.ABSOLUTE_ZERO_C}°C)”)

self._celsius = value

# Computed property (Fahrenheit)

@property

def fahrenheit(self):

return (self._celsius * 9/5) + 32

@fahrenheit.setter

def fahrenheit(self, value):

celsius = (value – 32) * 5/9

self.celsius = celsius # Reuse the celsius setter for validation

# Computed property (Kelvin)

@property

def kelvin(self):

return self._celsius + 273.15

@kelvin.setter

def kelvin(self, value):

celsius = value – 273.15

self.celsius = celsius

# Class method: alternative constructor from Fahrenheit

@classmethod

def from_fahrenheit(cls, fahrenheit):

celsius = (fahrenheit – 32) * 5/9

return cls(celsius)

# Class method: alternative constructor from Kelvin

@classmethod

def from_kelvin(cls, kelvin):

celsius = kelvin – 273.15

return cls(celsius)

# Static method: helper function

@staticmethod

def celsius_to_fahrenheit(celsius):

return (celsius * 9/5) + 32

# Instance method

def __str__(self):

return f”{self.celsius:.1f}°C / {self.fahrenheit:.1f}°F / {self.kelvin:.1f}K”

# Using the class

temp = Temperature(25)

print(temp) # 25.0°C / 77.0°F / 298.2K

print(temp.fahrenheit) # 77.0

temp.fahrenheit = 32

print(temp.celsius) # 0.0

temp.kelvin = 300

print(temp.celsius) # 26.85

# Alternative constructors

temp2 = Temperature.from_fahrenheit(212)

print(temp2) # 100.0°C / 212.0°F / 373.2K

temp3 = Temperature.from_kelvin(0)

print(temp3) # -273.2°C / -459.7°F / 0.0K

# Static method

print(Temperature.celsius_to_fahrenheit(100)) # 212.0

Method Type Comparison Table
Method TypeFirst ParameterDecoratorAccess InstanceAccess ClassCalled On
InstanceselfNoneYesYesInstance
Classcls@classmethodNoYesClass (or instance)
StaticNone@staticmethodNo (unless passed)No (unless passed)Class (or instance)
Propertyself@propertyYesYesInstance (like attribute)
When to Use Each Method Type
  • **Instance Method:** Most methods. Use when you need to access or modify instance-specific data.
  • **Class Method:** Alternative constructors, operations that affect the class as a whole, counters, factory patterns.
  • **Static Method:** Utility functions, validations, helper functions conceptually related to the class but not needing instance or class state.
  • **Property:** Computed attributes, validation on attribute assignment, read-only attributes, backward compatibility.
Common Mistakes with Methods
  • Forgetting the @classmethod decorator (then it becomes an instance method expecting self)
  • Forgetting the @staticmethod decorator (then it becomes an instance method expecting self, causing errors)
  • Calling a class method on an instance (works but is confusing)
  • Trying to access instance attributes in a class method without passing an instance
  • Creating infinite recursion in property setters (calling self.attribute = value inside the setter instead of self._attribute = value)

Python

# Common mistake: Infinite recursion in property

class Bad:

def __init__(self):

self.value = 0 # This calls the setter! (if property exists)

@property

def value(self):

return self._value

@value.setter

def value(self, v):

self.value = v # Recursion! Calls the setter again!

# Correct way

class Good:

def __init__(self):

self._value = 0 # Direct assignment to internal attribute

@property

def value(self):

return self._value

@value.setter

def value(self, v):

self._value = v # Assign to internal attribute

Check Your Understanding
  • What is the difference between an instance method and a class method?
  • How do you define a static method? When would you use one?
  • What is the purpose of the @property decorator?
  • Write a Rectangle class with width and height properties, plus a read-only area property.
  • What is an alternative constructor? Give an example.
  • Write a class method from_string that creates a Person instance from a string like “Ali,25”

⚡ Whisper

Four types of methods. Four tools in your OOP belt. Instance methods are your daily companions, working with the data of each object. Class methods speak to the blueprint itself, creating new instances or tracking collective information. Static methods are quiet helpers, providing utility without touching object state. Properties are the magicians, turning method calls into attribute access. Each has a role. Each has a place. Use instance methods for behavior that changes per object. Use class methods for factories and counters. Use static methods for utilities. Use properties for computed values and controlled access. Do not force one where another belongs. A static method pretending to be an instance method confuses readers. A property that modifies the world surprises users. Choose intentionally. Your code will sing.

Related posts