For those new to Python, have you ever felt confused by “decorators”? They are essentially tools that add “buffs” to your code! Today, I will help you grasp this concept with 5 key points and simple code examples, so you can get started right away.
1. Function Decorators: Adding “Functionality Coats” to Functions
1. @ Syntax: A “Sugar” to Simplify Code
If you want to add extra functionality to a function (like logging), you can do so without modifying the original function by simply adding an @decorator name:
# Define a decorator: print function execution prompt
def log_decorator(func):
def wrapper():
print("The function is about to run!")
func() # Call the original function
return wrapper
# Use @ to add a decorator to the hello function
@log_decorator
def hello():
print("Hello Python!")
hello() # Output: The function is about to run! → Hello Python!
2. The Principle of Decorators: Essentially “Function Nesting”
@log_decorator is essentially hello = log_decorator(hello), to put it simply:
Pass the original function to the decorator → The decorator returns a new function → The original function name points to the new function, triggering the added functionality when called.
2. Class Decorators: Adding “Dynamic Plugins” to Classes
Class decorators can add attributes/methods to classes in bulk, making it more flexible than directly modifying class code.
1. Example of Decorating a Class: Adding “Creation Time”
import time
class TimeDecorator:
def __init__(self, cls):
self.cls = cls # Receive the decorated class
def __call__(self): # Triggered when the class is instantiated
obj = self.cls()
obj.create_time = time.ctime() # Add new attribute
return obj
# Use class decorator to decorate MyClass
@TimeDecorator
class MyClass:
pass
obj = MyClass()
print(obj.create_time) # Output the creation time of the class instance
2. A Brief Introduction to Metaclasses
Metaclasses are “classes of classes”, and class decorators are a simplified version of metaclasses. If you need complex customization (like controlling the creation of all subclasses), you can delve deeper into metaclasses; for beginners, mastering class decorators is sufficient!
3. Built-in Decorators: Python’s Own “Tools”
These 3 decorators are extremely useful, remember them for direct use!
1. @property: Turning Methods into “Attributes”
Instead of writing get_xxx(), you can directly access the value using object.attribute, and you can also control modifications:
class Student:
def __init__(self, score):
self._score = score # Underscore indicates "private"
@property
def score(self): # Read property
return self._score
@score.setter # Allow modification (if not added, cannot modify)
def score(self, new_score):
if 0 <= new_score <= 100:
self._score = new_score
else:
print("Score must be between 0-100!")
s = Student(80)
print(s.score) # Direct read: 80
s.score = 95 # Valid modification
s.score = 105 # Invalid: print prompt
2. @staticmethod: Static Method
No need for self/cls, it has no relation to the class or instance, like a regular function:
class Math:
@staticmethod
def add(a, b): # No self/cls
return a + b
print(Math.add(2, 3)) # Direct call: 5
3. @classmethod: Class Method
Requires cls parameter, can only access class attributes, cannot access instance attributes:
class Car:
brand = "BYD" # Class attribute
@classmethod
def get_brand(cls): # Has cls
return cls.brand
print(Car.get_brand()) # Call: BYD
4. Parameterized Decorators: More Flexible “Customization”
Want to make the decorator support custom parameters? Just add a layer of function:
# First layer: receive decorator parameters
def log_decorator(custom_msg):
# Second layer: receive the original function
def wrapper(func):
# Third layer: actual execution
def inner():
print(custom_msg)
func()
return inner
return wrapper
# Add a parameterized decorator to hello
@log_decorator("Custom message: Starting execution!")
def hello():
print("Hello!")
hello() # Output: Custom message → Hello!
5. functools Module: Solving “Minor Troubles”
1. @functools.wraps: Preserve Original Function Information
Decorators can “lose” the original function’s name and docstring; adding this can preserve them:
import functools
def decorator(func):
@functools.wraps(func) # Key: preserve original function information
def wrapper():
func()
return wrapper
@decorator
def test():
"""This is the docstring for test"""
pass
print(test.__name__) # Output: test (if not added, it would be wrapper)
print(test.__doc__) # Output: docstring (if not added, it would be None)
2. @functools.lru_cache: Cache Results to Save Time
When repeatedly calling a function with the same parameters, it directly returns the cached result (for example, calculating Fibonacci):
import functools
@functools.lru_cache(maxsize=None) # Cache all results
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
print(fib(100)) # Super fast! The results of repeated calculations are cached
