Properties, class & static methods
@property getters and setters, cached_property, alternative constructors with @classmethod and @staticmethod.
In Java you'd write getBalance() and setBalance() for every field, "just in case". Python doesn't need that ceremony. You start with plain attributes, and if you later need validation or computed values, you turn the attribute into a property — without changing any code that uses the class. This lesson also covers the two other kinds of methods: class methods and static methods.
The problem: attributes with rules#
A negative price is a bug waiting to happen. You could make price private and add get_price() / set_price(), but then every caller must change, and code becomes noisier.
@property: computed attributes#
The @property decorator turns a method into a read-only attribute that is computed each time it's accessed:
Because area is computed from width and height, it can never get out of sync. Assigning to it fails, since there's no setter:
Setters: validation on assignment#
Add a setter with @<name>.setter. The real value is stored in an "internal" attribute, conventionally the same name with a leading underscore:
Notice that __init__ assigns self.price, not self._price, so the validation applies from the moment the object is created. Callers never knew anything changed — p.price = 699 works exactly as before. That's why Python developers happily start with public attributes.
You can also add a @price.deleter, though it's rarely needed.
Derived properties with setters#
A setter can update the underlying data in a different unit:
Caching expensive computations#
If a property is expensive and its inputs never change, functools.cached_property computes it once and stores the result on the instance:
Class methods: alternative constructors#
A regular method receives the instance (self). A class method receives the class (cls). Their most common job is providing alternative constructors — other ways to create an object:
You've already used built-in class methods like dict.fromkeys(), datetime.fromisoformat() and int.from_bytes(). Using cls(...) instead of the hard-coded class name means subclasses get instances of the right type.
Class methods can also manage class-level state, such as a registry or counter.
Static methods#
A static method receives neither the instance nor the class. It's a plain function that lives inside the class because it belongs there logically:
If a static method doesn't really belong to the class, a module-level function is just as good — Python doesn't force everything into classes.
Choosing the right kind of method#
Worked example: a validated bank account#
Common mistakes#
- Infinite recursion in a setter: writing
self.price = valueinside thepricesetter calls the setter again forever. Store intoself._price. - Adding parentheses:
r.area()fails withTypeError: 'int' object is not callablebecauser.areais already the value. - Slow or side-effecting properties: readers expect attribute access to be cheap and harmless. Use a method for expensive work or actions (
load(),send()). - Hard-coding the class name in class methods instead of using
cls. - Writing getters and setters for everything — use plain attributes until you need logic.
What's next#
Writing __init__, __repr__ and __eq__ by hand gets repetitive. Next, dataclasses generate all of that for you.
Check your understanding
Quick quiz
1.What does the
@propertydecorator let you do?2.What is the first parameter of a
@classmethod?3.When is a
@staticmethodappropriate?
Finished reading?
Mark this lesson complete to track your progress.