6.1 Decorators for Class Methods
Decorators can also be used for class methods. It's important to remember to correctly pass the self or cls arguments for class methods.
We haven't covered the nuances of classes yet, but I'd like you to know that decorators can be used this way.
def log_method_call(func):
def wrapper(self, *args, **kwargs):
print(f"Method call {func.__name__}")
return func(self, *args, **kwargs)
return wrapper
class MyClass:
@log_method_call
def say_hello(self):
print("Hello from MyClass!")
obj = MyClass()
obj.say_hello()
Explanation
Decorator (log_method_call): This decorator takes the method func and returns a new function wrapper that prints a message before calling the method.
Class method with decorator (say_hello): The say_hello method is wrapped with the log_method_call decorator, adding extra behavior when it's called.
Output:
Method call say_hello
Hello from MyClass!
6.2 Multiple Decorators
You can use multiple decorators for a single function, stacking them up. Decorators are applied in the reverse order of their declaration.
def decorator1(func):
def wrapper():
print("Decorator 1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2")
func()
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
Explanation
Decorators (decorator1 and decorator2): These decorators add their messages before calling the function func.
Function with decorators (say_hello): The say_hello function is wrapped by both decorators. First, decorator2 is applied, then decorator1.
Output:
# Decorator 1
# Decorator 2
Hello!
6.3 Built-in Decorators
Python provides several built-in decorators for standard tasks, like static methods, class methods, and properties.
@staticmethod
The @staticmethod decorator is used to create a static method, which doesn't require a class instance to be called.
class MyClass:
@staticmethod
def static_method():
print("This is a static method.")
MyClass.static_method()
@classmethod
The @classmethod decorator is used to create a class method, which takes the class (not instance) as its first argument.
class MyClass:
@classmethod
def class_method(cls):
print(f"This is a class method {cls.__name__}.")
MyClass.class_method()
@property
The @property decorator is used to create getters, setters, and deleters for attributes.
class MyClass:
def __init__(self, value):
self.hidden_value = value
@property
def value(self):
return self.hidden_value
@value.setter
def value(self, new_value):
self.hidden_value = new_value
obj = MyClass(10)
print(obj.value) # Output: 10
obj.value = 20
print(obj.value) # Output: 20
These are built-in decorators, their proper functioning is ensured by the Python interpreter itself.
6.4 Examples of Using Decorators
Logging
Decorators can be used to log function and method calls.
def log_call(func):
def wrapper(*args, **kwargs):
print(f"Calling function {func.__name__} with arguments {args} and {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_call
def add(x, y):
return x + y
print(add(2, 3))
Access Control
Decorators can be used to enforce access control to functions and methods.
def require_authentication(func):
def wrapper(*args, **kwargs):
if not args[0].is_authenticated:
raise PermissionError("User is not authenticated.")
return func(*args, **kwargs)
return wrapper
class User:
def __init__(self, is_authenticated):
self.is_authenticated = is_authenticated
@require_authentication
def view_profile(self):
print("User profile")
user = User(is_authenticated=True)
user.view_profile() # Successful call
user2 = User(is_authenticated=False)
user2.view_profile() # PermissionError: User is not authenticated.
Caching
Decorators can be used to cache function results.
def cache(func):
cached_results = {}
def wrapper(*args):
if args in cached_results:
return cached_results[args]
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(35))
GO TO FULL VERSION