@property / @classmethod / @staticmethod
| Since: | Python 2(2000) |
|---|
@property is a decorator that lets you use a method as a property, allowing you to add logic when accessing class attributes. @classmethod defines a method that receives the class itself (rather than an instance) as its first argument (cls). @staticmethod defines a utility method that does not depend on the class or any instance.
Syntax
class MyClass:
# Property (getter)
@property
def attr_name(self):
return self._attr_name
# Property (setter)
@attr_name.setter
def attr_name(self, value):
self._attr_name = value
# Class method (cls = the class itself)
@classmethod
def class_method(cls, arg):
...
# Static method (no self or cls needed)
@staticmethod
def static_method(arg):
...
Decorator List
| Decorator | Description |
|---|---|
| @property | Defines a method as a property (read-only access by default). |
| @attr_name.setter | Defines a method that runs when a value is assigned to the property. |
| @attr_name.deleter | Defines a method that runs when the property is deleted with the del statement. |
| @classmethod | Defines a method that receives the class itself (cls) as its first argument. |
| @staticmethod | Defines a static method that receives neither self nor cls. |
Sample Code
property_classmethod_1.py
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Cannot set a temperature below absolute zero.")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9 / 5 + 32
t = Temperature(100)
print(t.celsius)
print(t.fahrenheit)
t.celsius = 0
print(t.fahrenheit)
try:
t.celsius = -300
except ValueError as e:
print(e)
Running the code produces the following output:
python3 property_classmethod_1.py 100 212.0 32.0 Cannot set a temperature below absolute zero.
property_classmethod_2.py
class User:
_count = 0
def __init__(self, name, email):
self.name = name
self.email = email
User._count += 1
@classmethod
def get_count(cls):
return cls._count
@classmethod
def from_dict(cls, data):
return cls(data['name'], data['email'])
u1 = User('Gojo Satoru', 'gojo_satoru@wp-p.info')
u2 = User.from_dict({'name': 'Itadori Yuji', 'email': 'itadori_yuji@wp-p.info'})
print(User.get_count())
print(u1.name, u1.email)
print(u2.name, u2.email)
Running the code produces the following output:
python3 property_classmethod_2.py 2 Gojo Satoru gojo_satoru@wp-p.info Itadori Yuji itadori_yuji@wp-p.info
property_classmethod_3.py
class User:
def __init__(self, name, email):
self.name = name
self.email = email
@staticmethod
def is_valid_email(email):
return '@' in email and '.' in email
print(User.is_valid_email('gojo_satoru@wp-p.info'))
print(User.is_valid_email('invalid'))
u = User('Fushiguro Megumi', 'fushiguro_megumi@wp-p.info')
print(u.is_valid_email(u.email))
Running the code produces the following output:
python3 property_classmethod_3.py True False True
Common Mistakes
Common Mistake 1: Directly modifying a private variable without @property
Variables prefixed with an underscore (e.g., _celsius) signal "private" by Python convention, but the language provides no real enforcement. Directly modifying them bypasses any validation defined in a setter.
mistake1_ng.py
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
t = Temperature(100)
t._celsius = -999
print(t._celsius)
Running the code produces the following output:
python3 mistake1_ng.py -999
mistake1_ok.py
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Cannot set a temperature below absolute zero.")
self._celsius = value
t = Temperature(100)
try:
t.celsius = -999
except ValueError as e:
print(e)
Running the code produces the following output:
python3 mistake1_ok.py Cannot set a temperature below absolute zero.
Common Mistake 2: Choosing the wrong decorator between @classmethod and @staticmethod
Use @classmethod when the method needs access to the class itself (via cls). Use @staticmethod when the method uses neither the class nor any instance state. A @staticmethod cannot access the class through cls.
class User:
_count = 0
def __init__(self, name):
self.name = name
User._count += 1
@staticmethod
def get_count():
return User._count
u1 = User('Gojo Satoru')
u2 = User('Itadori Yuji')
print(User.get_count())
mistake2_ok.py
class User:
_count = 0
def __init__(self, name):
self.name = name
User._count += 1
@classmethod
def get_count(cls):
return cls._count
u1 = User('Gojo Satoru')
u2 = User('Itadori Yuji')
print(User.get_count())
Running the code produces the following output:
python3 mistake2_ok.py 2
Notes
Using @property lets you automatically add validation or conversion logic whenever an instance variable is accessed. Even if you change the internal implementation of a class, external code can still access it using the same property syntax, making it easier to maintain API compatibility.
@classmethod is frequently used as an alternative constructor (factory method). A common pattern is defining a from_dict() method that creates an instance from a dictionary or JSON data.
@staticmethod is used for logic that belongs to the class conceptually but does not use any class or instance state. If a method uses neither self nor cls, marking it as a staticmethod makes it clear that it belongs in the class scope rather than outside the class. Use @classmethod when you need access to the class itself, and @staticmethod when you do not.
If you find any errors or copyright issues, please contact us.