Fundamentals 8 min read

Understanding Python Decorators: Introduction, Common Examples, and Summary

This article introduces Python decorators, explains their purpose, and provides practical examples of common decorators such as @staticmethod, @property, @lru_cache, @wraps, @retry, @contextmanager, @dataclass, @cached_property, @singledispatch, and @timeit, concluding with a summary of their benefits for clean, maintainable code.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
Understanding Python Decorators: Introduction, Common Examples, and Summary

1. Introduction to Decorators Decorators are functions that accept another function as an argument and return a new function, allowing modification of behavior such as logging, performance testing, or transaction handling. They are applied using the @ syntax before a function definition.

2. Decorator Examples

@staticmethod and @classmethod Use when a method does not need to access instance or class state.

class MathUtil:
    @staticmethod
    def add(a, b):
        return a + b
    @classmethod
    def subtract(cls, a, b):
        return a - b

print(MathUtil.add(1, 2))  # 输出 3
print(MathUtil.subtract(3, 1))  # 输出 2

@property Use to turn a method into a read‑only attribute, optionally providing a setter for controlled modification.

class Person:
    def __init__(self, age):
        self._age = age
    @property
    def age(self):
        return self._age
    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError("Age cannot be negative")
        self._age = value
p = Person(30)
print(p.age)  # 输出 30
p.age = 35
print(p.age)  # 输出 35

@lru_cache Use to cache function results and avoid repeated computation, especially useful for recursive or intensive tasks.

from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))  # 输出 55

@functools.wraps Use inside a decorator to preserve the wrapped function's metadata such as name and docstring.

from functools import wraps
def debug(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@debug
def say_hello(name):
    """Say hello to someone."""
    print(f"Hello, {name}!")
say_hello("Alice")  # 输出 Calling say_hello
                     #      Hello, Alice!

@retry Use to automatically retry a function that may fail, such as network requests.

from functools import wraps
def retry(times=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"Failed, retrying... {e}")
            raise Exception("Max retries exceeded")
        return wrapper
    return decorator

@retry() Example of a function that randomly fails and can be wrapped with a retry mechanism.

def get_data(url):
    # 模拟网络请求,这里使用随机抛出异常来模拟失败情况
    import random
    if random.random() < 0.5:
        raise Exception("Network error")
    return "Data retrieved"
print(get_data("http://example.com"))  # 输出 Data retrieved

@contextmanager Use to create a context manager that handles resource acquisition and release automatically.

from contextlib import contextmanager
@contextmanager
def managed_file(name):
    try:
        f = open(name, 'r')
        yield f
    finally:
        f.close()
with managed_file('test.txt') as f:
    for line in f:
        print(line)  # 假设 test.txt 中有内容

@dataclass Use to simplify class definitions by automatically generating __init__, __repr__, __eq__, and other methods.

from dataclasses import dataclass
@dataclass
class Point:
    x: int
    y: int
p = Point(1, 2)
print(p)  # 输出 Point(x=1, y=2)

@cached_property Use to cache the result of a property method after the first access.

from functools import cached_property
class Circle:
    def __init__(self, radius):
        self.radius = radius
    @cached_property
    def area(self):
        return 3.14 * (self.radius ** 2)
c = Circle(5)
print(c.area)  # 输出 78.5

@singledispatch Use to implement generic functions that dispatch based on the type of the first argument.

from functools import singledispatch
@singledispatch
def add(a, b):
    raise NotImplementedError("Unsupported type")
@add.register(int)
def _(a, b):
    return a + b
@add.register(str)
def _(a, b):
    return a + b
print(add(1, 2))  # 输出 3
print(add("Hello, ", "World!"))  # 输出 Hello, World!

@timeit Use to measure the execution time of a function for performance analysis.

import time
def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.6f}s")
        return result
    return wrapper
@timeit
def sleep_and_return(seconds):
    time.sleep(seconds)
    return "Done sleeping"
print(sleep_and_return(1))  # 输出 sleep_and_return took 1.000000s
                             # 输出 Done sleeping

3. Summary Decorators can simplify code, extend functionality, and make programs easier to maintain and evolve. The examples above aim to help you understand and apply decorators effectively, enhancing your Python programming skills.

PythonprogrammingCode Examplefundamentalsdecorator
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.