從零到精通:Python 裝飾器的完整指南與實(shí)戰(zhàn)應(yīng)用
裝飾器是Python中最優(yōu)雅卻最容易被誤解的特性之一。許多初學(xué)者對(duì)"裝飾"這個(gè)概念感到困惑,高級(jí)開發(fā)者則可能陷入過度使用裝飾器的陷阱。本文將從基礎(chǔ)原理出發(fā),逐步深入裝飾器的各種應(yīng)用場(chǎng)景,幫助你真正掌握這項(xiàng)強(qiáng)大的工具。

一、裝飾器的本質(zhì)原理
1. 什么是裝飾器
裝飾器本質(zhì)上是一個(gè)函數(shù),它接收另一個(gè)函數(shù)作為參數(shù),并返回一個(gè)新函數(shù)。這個(gè)新函數(shù)通常會(huì)在原函數(shù)執(zhí)行前后進(jìn)行某些操作。
# 最簡(jiǎn)單的裝飾器
defmy_decorator(func):
defwrapper():
print("執(zhí)行前")
func()
print("執(zhí)行后")
return wrapper
@my_decorator
defsay_hello():
print("Hello!")
# 等同于:say_hello = my_decorator(say_hello)
say_hello()
# 輸出:
# 執(zhí)行前
# Hello!
# 執(zhí)行后2. 保留原函數(shù)的元數(shù)據(jù)
使用裝飾器后,原函數(shù)的元數(shù)據(jù)(如函數(shù)名、文檔字符串)會(huì)丟失。使用functools.wraps可以解決這個(gè)問題。
from functools import wraps
defmy_decorator(func):
@wraps(func)
defwrapper(*args, **kwargs):
"""這是wrapper函數(shù)"""
print("執(zhí)行前")
result = func(*args, **kwargs)
print("執(zhí)行后")
return result
return wrapper
@my_decorator
defadd(a, b):
"""將兩個(gè)數(shù)相加"""
return a + b
print(add.__name__) # 'add',而不是'wrapper'
print(add.__doc__) # '將兩個(gè)數(shù)相加'二、裝飾器的常見應(yīng)用場(chǎng)景
1. 性能監(jiān)測(cè)裝飾器
import time
from functools import wraps
deftimeit(func):
@wraps(func)
defwrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 耗時(shí):{end - start:.4f}秒")
return result
return wrapper
@timeit
defslow_function():
time.sleep(1)
return"完成"
result = slow_function()2. 日志記錄裝飾器
import logging
from functools import wraps
deflog_calls(level=logging.INFO):
defdecorator(func):
@wraps(func)
defwrapper(*args, **kwargs):
logging.log(level, f"調(diào)用 {func.__name__} 參數(shù):{args}, {kwargs}")
result = func(*args, **kwargs)
logging.log(level, f"{func.__name__} 返回值:{result}")
return result
return wrapper
return decorator
@log_calls(level=logging.DEBUG)
defprocess_data(data):
return data.upper()
process_data("hello")3. 函數(shù)參數(shù)驗(yàn)證裝飾器
from functools import wraps
defvalidate_types(**type_checks):
defdecorator(func):
@wraps(func)
defwrapper(*args, **kwargs):
for arg_name, expected_type in type_checks.items():
if arg_name in kwargs:
ifnot isinstance(kwargs[arg_name], expected_type):
raise TypeError(f"{arg_name} 必須是 {expected_type}")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_types(name=str, age=int)
defcreate_user(name, age):
returnf"創(chuàng)建用戶:{name}, 年齡:{age}"
create_user(name="Alice", age=25) # 正常
# create_user(name="Alice", age="25") # 會(huì)拋出TypeError4. 緩存裝飾器(Memoization)
from functools import wraps, lru_cache
# 簡(jiǎn)單的緩存實(shí)現(xiàn)
defcache(func):
cached_data = {}
@wraps(func)
defwrapper(*args):
if args notin cached_data:
cached_data[args] = func(*args)
return cached_data[args]
wrapper.cache = cached_data
return wrapper
@cache
deffibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 使用內(nèi)置的lru_cache
@lru_cache(maxsize=128)
deffibonacci_builtin(n):
if n < 2:
return n
return fibonacci_builtin(n-1) + fibonacci_builtin(n-2)
print(fibonacci(30)) # 快速計(jì)算
print(fibonacci.cache) # 查看緩存5. 重試裝飾器
from functools import wraps
import time
import random
defretry(max_attempts=3, delay=1, backoff=2):
defdecorator(func):
@wraps(func)
defwrapper(*args, **kwargs):
current_delay = delay
last_exception = None
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
if attempt < max_attempts - 1:
print(f"嘗試 {attempt + 1} 失敗,{current_delay}秒后重試...")
time.sleep(current_delay)
current_delay *= backoff
raise last_exception
return wrapper
return decorator
@retry(max_attempts=3, delay=1, backoff=2)
defunstable_api_call():
if random.random() < 0.7:
raise Exception("API調(diào)用失敗")
return"成功"
result = unstable_api_call()6. 權(quán)限驗(yàn)證裝飾器
from functools import wraps
defrequire_permission(*required_perms):
defdecorator(func):
@wraps(func)
defwrapper(user, *args, **kwargs):
ifnot hasattr(user, 'permissions'):
raise PermissionError("用戶無(wú)權(quán)限屬性")
user_perms = set(user.permissions)
required = set(required_perms)
ifnot required.issubset(user_perms):
raise PermissionError(f"用戶缺少權(quán)限:{required - user_perms}")
return func(user, *args, **kwargs)
return wrapper
return decorator
classUser:
def__init__(self, name, permissions):
self.name = name
self.permissions = permissions
@require_permission('admin', 'delete')
defdelete_user(user, user_id):
returnf"{user.name} 刪除了用戶 {user_id}"
admin_user = User("Admin", ['admin', 'delete', 'read', 'write'])
delete_user(admin_user, 123) # 成功7. 速率限制裝飾器
from functools import wraps
import time
from collections import defaultdict
defrate_limit(calls_per_second=1):
min_interval = 1.0 / calls_per_second
last_called = [0.0]
defdecorator(func):
@wraps(func)
defwrapper(*args, **kwargs):
elapsed = time.time() - last_called[0]
if elapsed < min_interval:
time.sleep(min_interval - elapsed)
last_called[0] = time.time()
return func(*args, **kwargs)
return wrapper
return decorator
@rate_limit(calls_per_second=2)
defapi_call():
print(f"API調(diào)用時(shí)間:{time.time()}")
return"成功"
for _ in range(5):
api_call()8. 方法裝飾器和類裝飾器
from functools import wraps
# 方法裝飾器
defmethod_logger(func):
@wraps(func)
defwrapper(self, *args, **kwargs):
print(f"調(diào)用方法 {self.__class__.__name__}.{func.__name__}")
return func(self, *args, **kwargs)
return wrapper
# 類裝飾器
defadd_str_repr(cls):
def__str__(self):
attrs = ', '.join(f"{k}={v}"for k, v in self.__dict__.items())
returnf"{cls.__name__}({attrs})"
cls.__str__ = __str__
return cls
@add_str_repr
classPerson:
def__init__(self, name, age):
self.name = name
self.age = age
@method_logger
defgreet(self):
returnf"你好,我是{self.name}"
person = Person("Alice", 25)
print(person) # Person(name=Alice, age=25)
person.greet() # 調(diào)用方法 Person.greet三、高級(jí)裝飾器模式
9. 可選參數(shù)的裝飾器
from functools import wraps
defsmart_decorator(func=None, *, enabled=True):
defdecorator(f):
ifnot enabled:
return f
@wraps(f)
defwrapper(*args, **kwargs):
print("裝飾器生效")
return f(*args, **kwargs)
return wrapper
if func isNone:
# 被調(diào)用時(shí)帶參數(shù):@smart_decorator(enabled=True)
return decorator
else:
# 被調(diào)用時(shí)不帶參數(shù):@smart_decorator
return decorator(func)
@smart_decorator
deffunc1():
return"func1"
@smart_decorator(enabled=False)
deffunc2():
return"func2"10. 鏈?zhǔn)窖b飾器
defdecorator_a(func):
@wraps(func)
defwrapper(*args, **kwargs):
print("A前")
result = func(*args, **kwargs)
print("A后")
return result
return wrapper
defdecorator_b(func):
@wraps(func)
defwrapper(*args, **kwargs):
print("B前")
result = func(*args, **kwargs)
print("B后")
return result
return wrapper
@decorator_a
@decorator_b
defmy_func():
print("執(zhí)行函數(shù)")
return"結(jié)果"
my_func()
# 輸出:
# A前
# B前
# 執(zhí)行函數(shù)
# B后
# A后四、結(jié)尾
裝飾器是Python中最強(qiáng)大的工具之一,也是代碼復(fù)用和關(guān)注點(diǎn)分離的最佳實(shí)踐。從簡(jiǎn)單的日志記錄到復(fù)雜的權(quán)限驗(yàn)證,裝飾器都能優(yōu)雅地解決問題。然而,要記住裝飾器的目的是讓代碼更清晰、更易維護(hù)。過度使用裝飾器反而會(huì)增加代碼復(fù)雜性。




































