Studying decorators in Python: Functions that change the behavior of other functions, adding functionality.

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

A self-study guide for Python 3 compiled from the materials on this site. Primarily intended for those who want to learn the Python programming language from scratch.

What are decorators in Python

Decorators are a powerful Python tool that allows you to change the behavior of functions, class methods, and other objects without changing their source code. They are an elegant way to add additional functionality to existing objects by wrapping them in other functions.

How Python decorators work

A decorator in Python is a function that takes another function as an argument and returns a new function. This new function usually extends or modifies the behavior of the original function by adding additional functionality to it.

 

Basic syntax of the decorator

def my_decorator(func):
    def wrapper():
print("Additional code before calling the function")
func()
print("Additional code after calling the function")
return wrapper

Using decorators with the @

symbol

A special syntax with the symbol @ is used to use the decorator. The decorator is placed immediately before the function definition:

@my_decorator
def say_hello():
print("Hello world!")

say_hello()

Execution result:

Additional code before calling the function
Hello, world!
Additional code after calling the function

Decorators with arguments

When the function being decorated accepts arguments, it is necessary to use *args and **kwargs to pass parameters:

def my_decorator(func):
    def wrapper(*args, **kwargs):
print("Additional code before calling the function")
result = func(*args, **kwargs)
print("Additional code after calling the function")
return result
    return wrapper

@my_decorator
def greet(name):
    print("Hello, {}!".format(name))

hello("The World")

Decorators for class methods

Decorators are successfully used to decorate class methods, which is especially useful for logging, checking access rights, or caching:

 

def my_decorator(func):
    def wrapper(*args, **kwargs):
print("Additional code before calling the method")
result = func(*args, **kwargs)
print("Additional code after calling the method")
return result
    return wrapper

class MyClass:
    @my_decorator
    def greet(self, name):
        print("Hello, {}!".format(name))

obj = MyClass()
obj.greet("The world")

Multiple decorators

Python allows you to apply multiple decorators to a single function. Decorators are applied from bottom to top:

 

def uppercase(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def bold(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return "<b>{}</b>".format(result)
    return wrapper

@bold
@uppercase
def greet(name):
    return "Hello, {}!".format(name)

print(greet("The World"))  # HELLO WORLD!</b>

Python's built-in Decorators

Python provides several built-in decorators:

@property

Turns a method into a property of a class:

class Circle:
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def area(self):
        return 3.14159 * self._radius ** 2

@staticmethod

Creates a static method of the class:

class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b

@classmethod

Creates a class method:

class Person:
    @classmethod
    def from_string(cls, name_str):
        return cls(name_str)

Decorators with parameters

To create decorators with parameters, a three-level structure is used:

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 say_hello():
print("Hello!")

Practical applications of decorators

Logging

import functools
import logging

def log_calls(func):
    @functools.wraps(func)
def wrapper(*args, **kwargs):
logging.info (f"Calling the function {func.__name__}")
return func(*args, **kwargs)
return wrapper

Measurement of execution time

import time
import functools

def timing(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} completed in {end - start:.4f} seconds")
return result
    return wrapper

Caching of results

import functools

def cache(func):
    cache_dict = {}
    
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        key = str(args) + str(kwargs)
        if key not in cache_dict:
            cache_dict[key] = func(*args, **kwargs)
        return cache_dict[key]
    return wrapper

Best practices for using decorators

  1. Use functiontools.wraps: This preserves the metadata of the original function
  2. Return the result: Always return the result of the original function
  3. Handle exceptions: Consider possible exceptions in the decorator
  4. Document the decorators: Add a docstring to explain the purpose of the decorator
Python decorators are a powerful tool for creating clean, readable code. They allow you to follow the DRY (Don't Repeat Yourself) principle and create reusable functionality that can be easily applied to various functions and methods.

 

categories

  • Introduction to Python
  • Python Programming Basics
  • Control Structures
  • Data Structures
  • Functions and Modules
  • Exception Handling
  • Working with Files and Streams
  • File System
  • Object-Oriented Programming (OOP)
  • Regular Expressions
  • Additional Topics
  • General Python Base