当前位置: 首页 > backend >正文

在python中装饰器的使用

文章目录

  • 一、前言
  • 二、基本知识
    • 2.1 闭包
    • 2.2 装饰器
  • 三、内置装饰器
    • 3.1 @property
    • 3.3 @classmethod
    • 3.4 @staticmethod
  • 四、自定义装饰器
    • 4.1 装饰器函数
      • 4.1.1 无参装饰器
      • 4.1.2 含参装饰器
      • 4.1.3 类方法装饰器
    • 4.2 类装饰器
      • 4.2.1 无参装饰器
      • 4.2.2 含参装饰器
    • 4.3 保留原函数信息
    • 4.4. 多个装饰器
  • 五、实践

一、前言

参考:https://pythonhowto.readthedocs.io/zh-cn/latest/decorator.html#id19
https://liaoxuefeng.com/books/python/functional/decorator/index.html

二、基本知识

2.1 闭包

闭包是嵌套函数,内部函数可以访问外部函数作用域的变量,他有两个显著特点:

  1. 嵌套函数
  2. 内部函数引用了外部函数作用域变量,并非全局变量
def outer():message = "Hello"def inner():  # 内部函数print(message)  # 访问外部变量return innerclosure = outer()
closure()  # 输出: Hello

2.2 装饰器

在python中,装饰器是一种增强现有函数行为的工具,在不修改原函数代码的前提下为函数动态增加新功能。

装饰器主要是利用了函数是一等公民,可以赋值给变量、作为参数传递或作为返回值的这一特性。

装饰器的工作原理:遇到类似@ decorator 语法时,会将被装饰的函数作为参数传递给装饰器函数,装饰器函数会返回一个新的函数来代替原始函数。

三、内置装饰器

3.1 @property

将方法转换为属性,实现安全访问和计算属性。如果不设置setter方法就是可读属性

class Circle:def __init__(self, radius):self._radius = radius@propertydef radius(self):return self._radius  # 直接通过 obj.radius 访问@radius.setterdef radius(self, value):if value < 0:raise ValueError("半径不能为负")self._radius = valuec = Circle(5)
print(c.radius)  # 输出: 5 (直接像属性一样调用)
c.radius = 10    # 触发 setter 方法

3.3 @classmethod

定义类方法,第一个参数为类本身cls,无需创建实例即可通过类直接调用

class Date:def __init__(self, year, month, day):self.year = yearself.month = monthself.day = day@classmethoddef from_string(cls, date_str):  year, month, day = map(int, date_str.split("-"))return cls(year, month, day)date = Date.from_string("2023-10-01")  # 通过类方法创建实例

3.4 @staticmethod

定义静态方法,无需实例或类参数

class MathUtils:@staticmethoddef add(a, b):return a + bprint(MathUtils.add(2, 3))  # 输出: 5 (直接通过类调用)

四、自定义装饰器

4.1 装饰器函数

4.1.1 无参装饰器

函数不带参数时可使用下面方式:

def my_decorator(func):  # 被装饰的函数def wrapper():print("Before function call")func()print("After function call")return wrapper@my_decorator
def say_hello():print("Hello!")say_hello()
# 输出:
# Before function call
# Hello!
# After function call

除了使用@语法糖,也可以手动装饰:

# 手动装饰
def say_hello():print("Hello!")decorated_func = my_decorator(say_hello)
decorated_func()

处理带参数的函数时可以使用*arg*kwargs来接受任意参数

def decorator(func):    # 被装饰的函数def wrapper(*args, **kwargs):   # 函数参数print("Decorator work")return func(*args, **kwargs)return wrapper@decorator
def add(a, b):return a + bprint(add(2, 3))  # 输出: Decorator work → 5

4.1.2 含参装饰器

如果要在装饰器传参,需要三层嵌套函数:

def repeat(n_times):  # 装饰器参数def decorator(func):  # 被装饰的函数def wrapper(*args, **kwargs):  # 函数参数for _ in range(n_times):result = func(*args, **kwargs)return resultreturn wrapperreturn decorator@repeat(n_times=3)
def greet(name):print(f"Hello {name}")greet("Alice")
# 输出 3 次: Hello Alice

4.1.3 类方法装饰器

类方法的函数装饰器和函数的函数装饰器类似

def decorator(func):    def wrapper(*args, **kwargs):print("Decorator work")return func(*args, **kwargs)return wrapperclass TestDecorator():@decoratordef add(self,a, b):return a + bobj=TestDecorator()
print(obj.add(2, 3))  
# Decorator work
# 5

4.2 类装饰器

4.2.1 无参装饰器

类装饰器使用__call__方法将类的实例变成一个用于装饰器的方法

class CountCalls:def __init__(self, func):self.func = funcself.calls = 0def __call__(self, *args, **kwargs):self.calls += 1print(f"Call {self.calls} of {self.func.__name__}")return self.func(*args, **kwargs)@CountCalls
def say_hello():print("Hello!")say_hello()  # 输出: Call 1 of say_hello → Hello!
say_hello()  # 输出: Call 2 of say_hello → Hello!

4.2.2 含参装饰器

装饰器类的参数需要通过__init__方法传递,所以被装饰的函数就只能在__call__方法中传入,为了把函数的参数传入,就在__call__方法中再封装一层

class CountCalls:def __init__(self, arg0):self.arg0 = arg0self.calls = 0def __call__(self, func):def wrapper(*args, **kwargs):self.calls += 1print(f"Call {self.calls} of {func.__name__}, {self.arg0}")return func(*args, **kwargs)return wrapper@CountCalls(arg0=0)
def say_hello():print("Hello!")say_hello()  # 输出: Call 1 of say_hello, 0 → Hello!
say_hello()  # 输出: Call 2 of say_hello, 0 → Hello!

4.3 保留原函数信息

装饰器会覆盖原函数的元信息,需要使用functools.wraps来保留:

from functools import wrapsdef decorator(func):@wraps(func)  # 关键点def wrapper(*args, **kwargs):"""Wrapper docstring"""print("Decorator action")return func(*args, **kwargs)return wrapper@decorator
def original():"""Original docstring"""passprint(original.__name__)  # 输出: original
print(original.__doc__)   # 输出: Original docstring

4.4. 多个装饰器

使用多个装饰器时,执行顺序按靠近函数的顺序依次执行

@decorator1    # 后执行
@decorator2    # 先执行
def my_func():pass
# 等价于: decorator1(decorator2(my_func))

五、实践

日志记录:

from functools import wrapsdef log_execution(func):@wraps(func)def wrapper(*args, **kwargs):print(f"Executing {func.__name__}...")result = func(*args, **kwargs)print(f"Finished executing {func.__name__}")return resultreturn wrapper@log_execution
def hello(name):print(f"hello {name}")hello("Lucy")
# Executing hello...
# hello Lucy
# Finished executing hello

重试机制:

import time
from functools import wrapsdef retry(max_attempts=3, delay=1):def decorator(func):@wraps(func)def wrapper(*args, **kwargs):attempts = 0while attempts < max_attempts:try:return func(*args, **kwargs)except Exception as e:print(f"尝试 {attempts+1}/{max_attempts} 失败: {e}")attempts += 1time.sleep(delay)raise RuntimeError("所有重试均失败")return wrapperreturn decorator@retry(max_attempts=2)
def call_unstable_api():import randomif random.random() < 0.8:raise ConnectionError("API 不可用")return "成功"call_unstable_api()  # 最多重试 2 次
http://www.xdnf.cn/news/1046.html

相关文章:

  • File工具总结
  • 悟空黑桃A邀请码(31187835)
  • VSCode远程图形化GDB
  • 算法 | 鲸鱼优化算法(WOA)与强化学习的结合研究
  • Dify-web开发思路
  • Pikachu靶场-SQL注入
  • STM32——相关软件安装
  • 【Linux】:HTTPS协议
  • 相机标定(输出相机内参和畸变参数)
  • ASP.NET 中防止用户多次登录的方法
  • wkhtmltopdf - HTML转PDF/图像命令行工具
  • python@staticmethod 是什么含义?
  • Coze平台​ 创建AI智能体的详细步骤指南
  • 多源异构网络安全数据(CAPEC、CPE、CVE、CVSS、CWE、ATTCK、D3FEND)的详细解析,包括其作用、数据内容及相互联系
  • 跨越1640年的诗路对话:谢灵运与瓯江山水的古今交响
  • EasyCVR视频汇聚平台安防监控问题排查:GB28181协议摄像头不在线的排查步骤
  • 基于Spring Boot实现文件秒传的完整方案
  • 5565反射内存网络产品
  • 【数据结构_11】二叉树(5)
  • JVM面试题学习
  • 算法复杂度
  • Oracle RMAN同步数据库Active database duplicate
  • ORION:通过视觉-语言指令动作生成的一个整体端到端自动驾驶框架
  • 操作系统线程入门
  • 前端中的浮动、定位与布局
  • 使用纯前端技术html+css+js实现一个蔬果商城的前端模板!
  • Github中项目的公开漏洞合集
  • Spring MVC 执行流程全解析:从请求到响应的七步走
  • 买卖股票的最佳时机(每日一题-简单)
  • SpringBoot中,声明式事务的使用