Python26 min read

Python Decorators

Understand decorators deeply: why they exist, how wrapper functions work, decorators with args, and real use cases like logging and access control.

Michael Brown
August 18, 2025
7.5k155

Decorators are a Python feature that lets you wrap a function to add extra behavior without changing the function’s core logic.

  Think of it like adding a cover on a phone:
  - the phone stays the same
  - the cover adds protection and extra features
  
  In code:
  - your function stays the same
  - the decorator adds timing, logging, validation, permissions, and more
  
  ## Step 1: functions can be passed around
  
  ```python
  def greet():
      return "hello"
  
  def call_function(func):
      return func()
  
  print(call_function(greet))
  ```
  
  Expected output:
  
  ```
  hello
  ```
  
  ## Step 2: a decorator is a function that returns a new function
  
  ```python
  def uppercase_decorator(func):
      def wrapper():
          result = func()
          return result.upper()
      return wrapper
  
  @uppercase_decorator
  def greet():
      return "hello from boston"
  
  print(greet())
  ```
  
  Expected output:
  
  ```
  HELLO FROM BOSTON
  ```
  
  ### What changed?
  
  - You still call `greet()`
  - But now it runs through `wrapper()` first
  
  ## Decorators with arguments (repeat example)
  
  ```python
  def repeat(times):
      def decorator(func):
          def wrapper(*args, **kwargs):
              for _ in range(times):
                  func(*args, **kwargs)
          return wrapper
      return decorator
  
  @repeat(3)
  def say_hi(name):
      print(f"Hi {name}!")
  
  say_hi("Tom")
  ```
  
  Expected output:
  
  ```
  Hi Tom!
  Hi Tom!
  Hi Tom!
  ```
  
  ## Real-world example: timing a function
  
  ```python
  import time
  
  def timer(func):
      def wrapper(*args, **kwargs):
          start = time.time()
          result = func(*args, **kwargs)
          end = time.time()
          print(f"Time: {end - start:.4f}s")
          return result
      return wrapper
  
  @timer
  def slow_function():
      time.sleep(1)
      print("Done!")
  
  slow_function()
  ```
  
  Expected output example:
  
  ```
  Done!
  Time: 1.00xxs
  ```
  
  ## Graph: decorator flow
  
  ```mermaid
  flowchart LR
    A[Call function] --> B[Decorator wrapper]
    B --> C[Original function]
    C --> D[Return result]
    D --> E[Decorator adds behavior]
  ```
  
  ## Common decorator mistakes
  
  - forgetting `*args, **kwargs` (breaks functions that take parameters)
  - not returning the function result when needed
  - writing decorators that hide errors
  
  In the next lesson, you will learn generators, which help you process large data efficiently without loading everything into memory.
#Python#Intermediate#Decorators