Properties, @staticmethod, @classmethod
Controlled attribute access with @property, shared utility with @staticmethod, and alternative constructors with @classmethod.
Overview
Key Points
- •@property: makes a method callable as an attribute no parentheses, can add validation in setter
- •@temperature.setter: defines the write path; @temperature.deleter: defines del behaviour
- •@classmethod: receives cls used for factory/alternative constructors, class-level operations
- •@staticmethod: receives neither self nor cls a utility function namespaced inside the class
- •Properties hide implementation detail switch from plain attribute to computed value transparently
- •Private by convention: prefix with _ (e.g. self._radius) to signal "internal use"
Examples
@property with getter and setter
class Circle:
def __init__(self, radius):
self._radius = radius # private by convention
@property
def radius(self): # getter accessed as obj.radius
return self._radius
@radius.setter
def radius(self, value): # setter called on obj.radius = x
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value
@property
def area(self): # read-only computed property
import math
return math.pi * self._radius ** 2
c = Circle(5)
print(c.radius) # 5
print(f"{c.area:.2f}") # 78.54
c.radius = 10
print(c.radius) # 10
# c.radius = -1 # raises ValueError@classmethod as alternative constructor
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str): # cls is the Date class itself
year, month, day = map(int, date_str.split("-"))
return cls(year, month, day) # equivalent to Date(year, month, day)
@classmethod
def today(cls):
from datetime import date
d = date.today()
return cls(d.year, d.month, d.day)
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
d1 = Date(2026, 1, 15)
d2 = Date.from_string("2026-06-20")
d3 = Date.today()
print(d1) # 2026-01-15
print(d2) # 2026-06-20@staticmethod for utility functions
class TemperatureConverter:
@staticmethod
def celsius_to_fahrenheit(c):
return c * 9 / 5 + 32
@staticmethod
def fahrenheit_to_celsius(f):
return (f - 32) * 5 / 9
@staticmethod
def is_valid_celsius(c):
return -273.15 <= c <= 1_000_000
# Call directly on the class no instance needed
print(TemperatureConverter.celsius_to_fahrenheit(100)) # 212.0
print(TemperatureConverter.fahrenheit_to_celsius(32)) # 0.0
print(TemperatureConverter.is_valid_celsius(-300)) # Falsestaticmethod vs classmethod: use staticmethod when you don't need cls or self; use classmethod when you need to create instances or access class-level data.
Common Mistakes
- Forgetting the @property decorator and calling the method with () or vice versa, adding () to a property
- Defining a setter without a getter first @prop.setter requires @property to be defined above it
- Using @staticmethod when you should use @classmethod if the function needs to create an instance, it needs cls
Note
Properties are especially useful for adding validation after the fact. If you start with a plain attribute and later need to validate it, change it to a property with a setter all existing code that reads or writes `obj.attr` continues to work unchanged.
Exercises
Write Python code to solve each exercise. Run it in your browser and check your output.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4