Skip to main content
Python for Beginners
CHAPTER 21 Beginner

Python Decorators

Updated: May 17, 2026
20 min read

# Python Decorators

Welcome to Chapter 21! Decorators let you modify or enhance functions without changing their source code. They're used extensively in frameworks like Flask, Django, and FastAPI.

---

1. Learning Objectives

  • Understand functions as first-class objects.
  • Create basic decorators.
  • Use @decorator syntax.
  • Chain multiple decorators.
  • Build practical decorators.

---

2. Functions as First-Class Objects

```python id="py21ex1" # Functions can be assigned to variables def greet(name): return f"Hello, {name}!"

sayhello = greet # Assign function to variable print(say_hello("Alice")) # Hello, Alice!

# Functions can be passed as arguments def apply(func, value): return func(value)

print(apply(len, "Python")) # 6

# Functions can return functions def multiplier(factor): def multiply(x): return x * factor return multiply

double = multiplier(2) print(double(5)) # 10

1234
---

## 3. Basic Decorator

python id="py21ex2" def mydecorator(func): def wrapper(*args, kwargs): print("⏳ Before function call") result = func(*args, kwargs) print("✅ After function call") return result return wrapper

@mydecorator # Same as: greet = mydecorator(greet) def greet(name): print(f"Hello, {name}!")

greet("Alice") # Output: # ⏳ Before function call # Hello, Alice! # ✅ After function call

123456
---

## 4. Practical Decorators

### Timer Decorator

python id="py21ex3" import time

def timer(func): def wrapper(*args, kwargs): start = time.time() result = func(*args, kwargs) elapsed = time.time() - start print(f"⏱️ {func.name} took {elapsed:.4f}s") return result return wrapper

@timer def slowfunction(): time.sleep(1) return "Done!"

slow_function()

12
### Logger Decorator

python id="py21ex4" from datetime import datetime

def logger(func): def wrapper(*args, kwargs): timestamp = datetime.now().strftime("%H:%M:%S") print(f"[{timestamp}] Calling {func.name}({args}, {kwargs})") result = func(*args, kwargs) print(f"[{timestamp}] {func.name_} returned {result}") return result return wrapper

@logger def add(a, b): return a + b

add(3, 5)

12
### Authentication Decorator

python id="py21ex5" def requireauth(func): def wrapper(user, *args, kwargs): if not user.get("isauthenticated"): print("❌ Access denied! Please login.") return None return func(user, *args, kwargs) return wrapper

@requireauth def viewdashboard(user): print(f"Welcome to dashboard, {user['name']}! 🎉")

viewdashboard({"name": "Alice", "isauthenticated": True}) viewdashboard({"name": "Bob", "is_authenticated": False})

1234
---

## 5. Decorators with Arguments

python id="py21ex6" 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 sayhello(): print("Hello!")

sayhello() # Prints "Hello!" three times

1234
---

## 6. Preserving Function Metadata

python id="py21ex7" from functools import wraps

def mydecorator(func): @wraps(func) # Preserves original function's name and docstring def wrapper(*args, kwargs): return func(*args, kwargs) return wrapper

@mydecorator def greet(name): """Greet a user by name.""" return f"Hello, {name}!"

print(greet.name) # greet (not 'wrapper') print(greet.doc_) # Greet a user by name.

1234
---

## 7. Chaining Decorators

python id="py21_ex8" def bold(func): @wraps(func) def wrapper(*args, kwargs): return f"<b>{func(*args, kwargs)}</b>" return wrapper

def italic(func): @wraps(func) def wrapper(*args, kwargs): return f"<i>{func(*args, kwargs)}</i>" return wrapper

@bold @italic def greet(name): return f"Hello, {name}!"

print(greet("Alice")) # <b><i>Hello, Alice!</i></b> # Applied bottom-up: italic first, then bold

1234
---

## 8. Class-based Decorators

python id="py21ex9" class CountCalls: def init(self, func): self.func = func self.count = 0 def call(self, *args, kwargs): self.count += 1 print(f"📊 {self.func.name} called {self.count} times") return self.func(*args, kwargs)

@CountCalls def sayhi(): print("Hi!")

sayhi() # called 1 times sayhi() # called 2 times sayhi() # called 3 times ``

---

9. MCQs with Answers

Q1: @decorator is equivalent to: A) func = decorator B) func = decorator(func) C) decorator = func D) func(decorator) Answer: B

Q2: Decorators modify functions: A) By editing source code B) By wrapping them C) By deleting them D) By copying them Answer: B

Q3: functools.wraps preserves: A) Speed B) Function metadata C) Memory D) Arguments Answer: B

Q4: Decorator with arguments needs: A) 1 level of nesting B) 2 levels C) 3 levels D) No nesting Answer: C — outer(args) → decorator(func) → wrapper(*args).

Q5: Chained decorators apply: A) Top to bottom B) Bottom to top C) Random D) Parallel Answer: B — Innermost decorator applies first.

Q6: *args, kwargs in wrapper ensures: A) Decorator works with any function signature B) Speed C) Type safety D) Memory Answer: A

Q7: Class-based decorators use: A) init and call B) str C) iter D) get Answer: A

Q8: Real-world use of decorators: A) Flask routes B) Django views C) pytest fixtures D) All of these Answer: D

Q9: Can decorators be stacked? A) Yes B) No C) Only 2 D) Only 3 Answer: A

Q10: Without @wraps, func.name returns: A) Original name B) 'wrapper' C) None D) Error Answer: B

---

10. Interview Questions

  1. 1. What is a decorator? A function that takes a function and returns a modified version, adding behavior without changing the original code.
  1. 2. Why use @wraps? Preserves the original function's name, doc, and other metadata.
  1. 3. Real-world decorator examples? @app.route() (Flask), @loginrequired (Django), @property, @staticmethod, @classmethod.
  1. 4. Decorator with arguments pattern? Triple-nested: decorator_factory(args) → decorator(func) → wrapper(*args, kwargs).
  1. 5. Can decorators modify return values? Yes, the wrapper function can transform the return value before returning it.

---

11. Summary

  • Decorators wrap functions to add behavior without modifying source code.
  • Use @decorator syntax for clean application.
  • Always use @functools.wraps` to preserve metadata.
  • Decorators can accept arguments (triple-nested pattern).
  • Multiple decorators can be chained (applied bottom-up).

---

12. Next Chapter Recommendation

In Chapter 22: Working with Dates and Time, you'll master the datetime module for time-based operations! 🚀

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·