Decorators
Enhance functions and classes with Python decorators
What are Decorators?
- Functions that modify other functions or classes
- Use @ symbol to apply decorators
- Allow adding functionality without modifying original code
- Common for logging, timing, authentication, etc.
# Simple decorator
def my_decorator(func):
def wrapper():
print("Before function")
func()
print("After function")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Decorators with Arguments
- Use *args and **kwargs to accept any arguments
- Wrapper function passes arguments to original function
- Preserves function signature
- Can modify arguments before passing them
# Decorator with arguments
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello {name}!")
greet("Alice")
Built-in Decorators
- @staticmethod - method doesn't need instance or class
- @classmethod - method receives class as first argument
- @property - makes method accessible like an attribute
- Common in object-oriented programming
class MyClass:
class_var = "I am a class variable"
def __init__(obj, value):
obj._value = value
@staticmethod
def static_method():
print("Static method called")
@classmethod
def class_method(cls):
print(f"Class method: {cls.class_var}")
@property
def value(obj):
return obj._value
@value.setter
def value(obj, new_value):
obj._value = new_value
# Usage
obj = MyClass(10)
MyClass.static_method()
MyClass.class_method()
print(obj.value)
obj.value = 20
Practical Decorator Examples
- Timing decorator to measure execution time
- Logging decorator to track function calls
- Caching decorator to store results
- Authentication decorator for access control
import time
# Timing decorator
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
print("Function completed")
slow_function()
# Logging decorator
def logger(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
add(5, 3)