Skip to main content

Python's @property vs. @classmethod - A No-Nonsense Guide

· 3 min read
Serhii Hrekov
software engineer, creator, artist, programmer, projects founder

When you're diving into Python's Object-Oriented Programming (OOP) features, decorators like @property and @classmethod pop up all the time. They might seem similar at first glance, but they serve fundamentally different purposes. Getting them straight is key to writing clean, Pythonic code.

Let's cut through the noise and get straight to what they do and when you should use them.

@property: Making Methods Look Like Attributes

The @property decorator is all about managing attribute access. It lets you turn a class method into a "getter" for an attribute, meaning you can call it without parentheses. This is incredibly useful when you need to compute a value on the fly or want to protect an attribute from being modified directly.

Think of it as a way to add logic to attribute access.

A Straightforward Example

Imagine you have a Circle class. You want to store its radius, but you also want to easily get its diameter. The diameter is always just twice the radius, so you don't need to store it separately. This is a perfect use case for @property.

class Circle:
def __init__(self, radius):
self._radius = radius # A "private" attribute

@property
def diameter(self):
"""
Calculates the diameter based on the radius.
This is a 'getter'.
"""
print("Calculating diameter...")
return self._radius * 2

# --- How you use it ---
c = Circle(5)

# Notice we access 'diameter' like an attribute, not a method
# No parentheses needed!
print(f"The circle's radius is: {c._radius}") # Output: The circle's radius is: 5
print(f"The circle's diameter is: {c.diameter}") # Output: Calculating diameter...
# The circle's diameter is: 10

Without @property, you'd have to define get_diameter() and call it as c.get_diameter(). The decorator just makes your class's interface cleaner.

@classmethod: Working with the Class, Not the Instance

The @classmethod, on the other hand, is about methods that are bound to the class itself, not to a specific instance of the class. A regular method inside a class gets the instance as its first argument (conventionally named self). A class method gets the class as its first argument (conventionally named cls). This is most useful for creating factory methods—methods that create instances of the class using alternative ways, not just the standard init.

A Straightforward Example

Let's say we're creating a User class. Usually, you'd create a user with a username and password. But what if you also need to create a user from a dictionary, maybe from some JSON data? @classmethod is the perfect tool for this. Python

class User:
def __init__(self, username, password):
self.username = username
self.password = password

@classmethod
def from_dict(cls, user_data):
"""
A factory method to create a User instance from a dictionary.
'cls' here is the User class itself.
"""
return cls(user_data['username'], user_data['password'])

# --- How you use it ---

# Standard way to create an instance
user1 = User("john_doe", "password123")
print(f"User 1: {user1.username}") # Output: User 1: john_doe

# Alternative way using our class method
user_data_from_api = {"username": "jane_doe", "password": "supersecretpassword"}
user2 = User.from_dict(user_data_from_api)
print(f"User 2: {user2.username}") # Output: User 2: jane_doe

Notice we call User.from_dict(). We're calling it on the User class directly, not on an instance like user1.

The Bottom Line: Key Differences

propery-vs-classmethod-difference-table-python

So, next time you're deciding between the two, just ask yourself: • Do I want to make a method look like a simple attribute? Use @property. • Do I need a method that works with the class itself, maybe to create an instance in a special way? Use @classmethod.