05 Functions - 09 Built-in Decorators

Python provides several built-in function decorators that can be used to enhance functionality.

Some of the commonly used Python-provided function decorators, along with examples and use cases:

1. ’@staticmethod’

  • Purpose: Defines a static method that doesn’t require an instance of the class to be called. The method doesn’t take self as the first argument.
  • Use Case: Used when you need a method that belongs to the class, but does not access or modify the instance state.
>>> class MyClass:
...     @staticmethod
...     def greet(name):
...         return f"Hello, {name}!"
>>> 
>>> print(MyClass.greet("Alice"))
Hello, Alice!
>>> class MathUtility:
...     @staticmethod
...     def add(a, b):
...         return a + b
>>> 
>>> print(MathUtility.add(5, 7))
12

2. ’@classmethod’

  • Purpose: Defines a class method that takes the class as the first argument (cls) instead of an instance (self).
  • Use Case: Useful for methods that need to operate on the class itself, rather than an instance.
>>> class MyClass:
...     @classmethod
...     def class_info(cls):
...         return f"This is {cls.__name__} class."
>>> 
>>> print(MyClass.class_info())
This is MyClass class.
>>> class MyClass:
...     count = 0
...     
...     @classmethod
...     def increment_count(cls):
...         cls.count += 1
...         return cls.count
>>> 
>>> print(MyClass.increment_count())
1
>>> print(MyClass.increment_count())
2

3. ’@property’

  • Purpose: Used to define a method as a property, so it can be accessed like an attribute.
  • Use Case: Useful for defining read-only attributes or computing values on the fly.
>>> class Circle:
...     def __init__(self, radius):
...         self._radius = radius
...     
...     @property
...     def area(self):
...         return 3.14 * (self._radius ** 2)
>>> 
>>> c = Circle(5)
>>> print(c.area)  # (accessed as an attribute)
78.5

4. ’@functools.lru_cache()’

  • Purpose: A decorator that caches the results of expensive function calls, avoiding redundant computations for the same input.
  • Use Case: Use it for functions that are called repeatedly with the same arguments, such as in recursive algorithms.
>>> from functools import lru_cache
>>> 
>>> @lru_cache(maxsize=None)  # No cache size limit
... def fibonacci(n):
...     if n <= 1:
...         return n
...     return fibonacci(n - 1) + fibonacci(n - 2)
>>> 
>>> print(fibonacci(100))  # Fast calculation due to caching
354224848179261915075

5. ’@property.setter’

  • Purpose: Defines a setter for a property, allowing you to set a value for a property created using the @property decorator.
  • Use Case: Used to provide setter functionality for read-only properties.
>>> class Circle:
...     def __init__(self, radius):
...         self._radius = radius
...     
...     @property
...     def radius(self):
...         return self._radius
...     
...     @radius.setter
...     def radius(self, value):
...         if value > 0:
...             self._radius = value
...         else:
...             raise ValueError("Radius must be positive")
>>> 
>>> c = Circle(5)
>>> c.radius = 10  # Successfully sets the radius
>>> print(c.radius)
10

6. ’@functools.wraps()’

  • Purpose: A decorator used inside other decorators to preserve the metadata (e.g., __name__, __doc__) of the original function when it is wrapped.
  • Use Case: Used when you write custom decorators to ensure the original function’s name and docstring are not lost.
>>> from functools import wraps
>>> 
>>> def my_decorator(func):
...     @wraps(func)
...     def wrapper(*args, **kwargs):
...         print(f"Calling {func.__name__}")
...         return func(*args, **kwargs)
...     return wrapper
>>> 
>>> @my_decorator
... def greet(name):
...     """Greet a person."""
...     return f"Hello, {name}!"
>>> 
>>> print(greet.__name__)  # (not wrapper)
greet
>>> print(greet.__doc__)   # Greet a person (not the wrapper's docstring)
Greet a person

7. ’@functools.singledispatch()’

  • Purpose: A decorator used to define a generic function that can be customized based on the type of the first argument.
  • Use Case: Used for method overloading, where the function behaves differently depending on the type of its first argument.
>>> from functools import singledispatch
>>> 
>>> @singledispatch
... def func(arg):
...     print(f"Function for {type(arg)}: {arg}")
>>> 
>>> @func.register(int)
... def _(arg):
...     print(f"Function for int: {arg}")
>>> 
>>> @func.register(str)
... def _(arg):
...     print(f"Function for str: {arg}")
>>> 
>>> func(10)
Function for int: 10

>>> func("hello")
Function for str: hello

8. ’@functools.cache_property()’

  • Purpose: A decorator that turns a method into a property that is computed once and then cached for further accesses.
  • Use Case: When you want to compute a property only once and cache its value for efficiency.
>>> from functools import cached_property
>>> 
>>> class Square:
...     def __init__(self, side):
...         self.side = side
...     
...     @cached_property
...     def area(self):
...         print("Computing area...")
...         return self.side * self.side
>>> 
>>> sq = Square(4)
>>> print(sq.area)
Computing area...
16
>>> print(sq.area) # (cached, no "Computing area...")
16

Summary of Python’s Built-In Function Decorators

DecoratorPurposeUse Case
@staticmethodDefines a method that doesn’t take self or cls.Use when a method does not require instance state.
@classmethodDefines a method that operates on the class (cls).Use when the method needs to work with the class.
@propertyTurns a method into a read-only property.Use for computed properties.
@property.setterDefines a setter for a property.Use for setting a value for a property.
@functools.lru_cacheCaches results of expensive function calls.Use for optimizing functions with repeated calls.
@functools.wrapsPreserves metadata when wrapping a function.Use when writing custom decorators.
@functools.singledispatchDefines a function that changes behavior based on argument type.Use for function overloading.
@functools.cache_propertyTurns a method into a property with caching.Use for expensive computed properties.