Python装饰器自学全解

发布时间:2026/9/24 17:57:55
Python装饰器自学全解 1. 关于装饰器这一概念的基本知识, 其中的第一小节内容探讨了这样一个问题, 即究竟什么是装饰器?装饰器属于一种设计模式, 这种设计模式允许在不修改原始函数代码的前提情况下, 往函数内部添加全新的功能内容, 它属于语法糖的一种范畴, 是基于闭包机制以及高阶函数来实现相关效果的。1.第2点, 装饰器究竟是什么东西。从本质上来讲, 这个可调用对象就是装饰器。它的主要功能是接收一个函数当作参数。然后它会返回一个新的函数给你。def decorator(func):def wrapper(*args, **kwargs):# 添加新功能result func(*args, **kwargs)# 添加新功能return resultreturn wrapper2. 关于装饰器的基础知识这一部分, 我们来到了第二小节, 也就是编号为2.1的函数基础知识回顾环节。# 1. 函数是一等公民可以赋值给变量def greet(name):return fHello, {name}!say_hello greet # 函数赋值给变量print(say_hello(Alice)) # Hello, Alice!# 2. 函数可以作为参数传递def apply_function(func, value):return func(value)def square(x):return x * xprint(apply_function(square, 5)) # 25# 3. 函数可以嵌套定义def outer():def inner():return Inner functionreturn inner()# 4. 闭包函数可以记住它被创建时的环境def make_multiplier(n):def multiplier(x):return x * nreturn multipliertimes_3 make_multiplier(3)print(times_3(4)) # 123. 针对基础级别的装饰器这一块内容, 我们展开详细讲解。# 最简单的装饰器def my_decorator(func):def wrapper():print(Something is happening before the function is called.)func()print(Something is happening after the function is called.)return wrappermy_decoratordef say_hello():print(Hello!)say_hello()# 输出# Something is happening before the function is called.# Hello!# Something is happening after the function is called.3.这是关于第二个部分的内容, 主要是讲解那些带有参数的函数装饰器。def timer_decorator(func):import timedef wrapper(*args, **kwargs):start_time time.time()result func(*args, **kwargs)end_time time.time()print(f{func.__name__} executed in {end_time - start_time:.4f} seconds)return resultreturn wrappertimer_decoratordef slow_function(seconds):time.sleep(seconds)return fSlept for {seconds} secondsprint(slow_function(2))# 输出# slow_function executed in 2.0023 seconds# Slept for 2 seconds3.3 带参数的装饰器def repeat(num_times):装饰器工厂函数返回一个装饰器def decorator_repeat(func):def wrapper(*args, **kwargs):for _ in range(num_times):result func(*args, **kwargs)return resultreturn wrapperreturn decorator_repeatrepeat(num_times3)def greet(name):print(fHello, {name}!)greet(Alice)# 输出# Hello, Alice!# Hello, Alice!# Hello, Alice!3.4 保留函数元信息from functools import wrapsdef preserve_metadata_decorator(func):wraps(func) # 使用wraps保留原函数的元信息def wrapper(*args, **kwargs):包装函数的文档print(fCalling {func.__name__})return func(*args, **kwargs)return wrapperpreserve_metadata_decoratordef calculate_sum(a, b):计算两个数的和return a bprint(calculate_sum.__name__) # calculate_sum如果不使用wraps会是wrapperprint(calculate_sum.__doc__) # 计算两个数的和print(calculate_sum(3, 4)) # 74. 所谓类装饰器这一章节的内容, 其主题是讨论4.1部分, 也就是要把一个类当作装饰器来使用的情景。class TimerDecorator:def __init__(self, func):self.func funcdef __call__(self, *args, **kwargs):import timestart_time time.time()result self.func(*args, **kwargs)end_time time.time()print(fExecution time: {end_time - start_time:.4f} seconds)return resultTimerDecoratordef long_running_operation():time.sleep(1)return Operation completedprint(long_running_operation())4.2 带参数的类装饰器class RetryDecorator:def __init__(self, max_retries3):self.max_retries max_retriesdef __call__(self, func):def wrapper(*args, **kwargs):for attempt in range(self.max_retries):try:return func(*args, **kwargs)except Exception as e:if attempt self.max_retries - 1:raiseprint(fAttempt {attempt 1} failed: {e}. Retrying...)return Nonereturn wrapperRetryDecorator(max_retries3)def unstable_function():import randomif random.random() 0.7:raise ValueError(Random failure!)return Success!print(unstable_function())5. 关于另外的一些装饰器的应用, 在章节的第五点第一个小标题部分, 提到了存在多个装饰器这样的状况。def decorator1(func):wraps(func)def wrapper(*args, **kwargs):print(Decorator 1: Before)result func(*args, **kwargs)print(Decorator 1: After)return resultreturn wrapperdef decorator2(func):wraps(func)def wrapper(*args, **kwargs):print(Decorator 2: Before)result func(*args, **kwargs)print(Decorator 2: After)return resultreturn wrapperdecorator1decorator2def say_hello():print(Hello!)say_hello()# 输出# Decorator 1: Before# Decorator 2: Before# Hello!# Decorator 2: After# Decorator 1: After# 注意装饰器从下往上执行5.第2部分要讲的是装饰器以及与类方法相关的这些内容。def method_decorator(func):wraps(func)def wrapper(self, *args, **kwargs):print(fCalling method {func.__name__} on {self})return func(self, *args, **kwargs)return wrapperclass Calculator:def __init__(self, value0):self.value valuemethod_decoratordef add(self, x):self.value xreturn self.valuemethod_decoratordef multiply(self, x):self.value * xreturn self.valuecalc Calculator(10)print(calc.add(5)) # 15print(calc.multiply(2)) # 306. 关于装饰器的应用示例第6点1, 也就是缓存这个装饰器。from functools import lru_cache# 使用内置的lru_cache装饰器lru_cache(maxsize128)def fibonacci(n):if n 2:return nreturn fibonacci(n-1) fibonacci(n-2)# 自定义缓存装饰器def memoize(func):cache {}wraps(func)def wrapper(*args, **kwargs):key str(args) str(kwargs)if key not in cache:cache[key] func(*args, **kwargs)return cache[key]return wrappermemoizedef expensive_computation(x):print(fComputing for {x}...)import timetime.sleep(1)return x * xprint(expensive_computation(5)) # 会打印Computing for 5...print(expensive_computation(5)) # 直接从缓存返回不会打印6.这是一个用于权限验证装饰器的条目。def require_permission(permission):def decorator(func):wraps(func)def wrapper(user, *args, **kwargs):if permission not in user.get(permissions, []):raise PermissionError(fUser lacks {permission} permission)return func(user, *args, **kwargs)return wrapperreturn decoratorclass UserManager:require_permission(admin)def delete_user(self, user, target_user):return fUser {target_user} deleted by {user[name]}require_permission(editor)def edit_content(self, user, content):return fContent edited by {user[name]}admin_user {name: Alice, permissions: [admin, editor]}editor_user {name: Bob, permissions: [editor]}regular_user {name: Charlie, permissions: []}manager UserManager()print(manager.delete_user(admin_user, old_user)) # 正常执行# print(manager.delete_user(editor_user, old_user)) # 抛出PermissionError6.这第3项是关于日志装饰器的一个内容说明。import loggingfrom datetime import datetimelogging.basicConfig(levellogging.INFO)def log_decorator(func):wraps(func)def wrapper(*args, **kwargs):start_time datetime.now()logging.info(fStarting {func.__name__} at {start_time})try:result func(*args, **kwargs)end_time datetime.now()duration (end_time - start_time).total_seconds()logging.info(fFinished {func.__name__} in {duration:.2f}s)return resultexcept Exception as e:logging.error(fError in {func.__name__}: {e})raisereturn wrapperlog_decoratordef process_data(data):import timetime.sleep(0.5)return [x * 2 for x in data]print(process_data([1, 2, 3, 4, 5]))6.第4部分是关于类型检查装饰器的内容。def type_check(**types):def decorator(func):wraps(func)def wrapper(*args, **kwargs):# 检查位置参数for i, (arg, (param_name, expected_type)) in enumerate(zip(args, types.items())):if not isinstance(arg, expected_type):raise TypeError(fArgument {param_name} must be {expected_type}, fgot {type(arg)})# 检查关键字参数for param_name, value in kwargs.items():if param_name in types and not isinstance(value, types[param_name]):raise TypeError(fArgument {param_name} must be {types[param_name]}, fgot {type(value)})return func(*args, **kwargs)return wrapperreturn decoratortype_check(xint, yint)def add_numbers(x, y):return x yprint(add_numbers(5, 3)) # 8# print(add_numbers(5, 3)) # 抛出TypeError7. 关于装饰器的调试工作以及测试环节, 其中需要着重关注的是第7.1小节所涉及的如何对装饰器进行调试这一具体问题的展开说明。def debug_decorator(func):wraps(func)def wrapper(*args, **kwargs):print(f[DEBUG] Calling {func.__name__})print(f[DEBUG] Args: {args})print(f[DEBUG] Kwargs: {kwargs})result func(*args, **kwargs)print(f[DEBUG] {func.__name__} returned: {result})return resultreturn wrapperdebug_decoratordef divide(a, b):return a / bdivide(10, 2)7.2 测试装饰器import unittestdef validate_input(min_value0, max_value100):def decorator(func):wraps(func)def wrapper(value):if not (min_value value max_value):raise ValueError(fValue must be between {min_value} and {max_value})return func(value)return wrapperreturn decoratorvalidate_input(min_value0, max_value100)def process_score(score):return Pass if score 60 else Failclass TestDecorator(unittest.TestCase):def test_valid_score(self):self.assertEqual(process_score(75), Pass)self.assertEqual(process_score(45), Fail)def test_invalid_score(self):with self.assertRaises(ValueError):process_score(150)with self.assertRaises(ValueError):process_score(-10)if __name__ __main__:unittest.main()8. 要避免出现, 在循环里面去定义装饰器的这样的操作行为。# 陷阱在循环中定义装饰器def create_decorators():decorators []for i in range(3):def my_decorator(func):def wrapper():print(fDecorator {i})return func()return wrapperdecorators.append(my_decorator)return decorators # 所有装饰器都会打印Decorator 2# 正确做法使用闭包捕获变量def create_decorator_fixed(n):def my_decorator(func):def wrapper():print(fDecorator {n})return func()return wrapperreturn my_decorator9. 总结装饰器是中强大且灵活的特性它允许我们增强函数的功能, 意味着我们可以做到无需修改原始函数代码, 从而实现代码复用将通用功能封装在装饰器里面。这能够保持代码整洁, 把关注点进行分离, 使代码变得更易于维护。它有助于实现面向切面编程, 比如进行日志处理, 或者执行权限验证等等。掌握装饰器需要理解

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询