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

python-装饰器

python-装饰器

  • 一、闭包
  • 二、装饰器
    • 1. 编辑统计运行时间装饰器
    • 2. 编辑日志记录装饰器
  • 三、保留元信息
  • 四、带参数的装饰器
  • 五、用类实现装饰器
  • 总结


一、闭包

闭包满足的条件:

  1. 必须要有内外函数
  2. 内函数必须引用外函数变量
  3. 外函数必须返回内函数

变量的生命周期

A = 100def outer():x = 100print(f"this is {x}")outer()
# print(x)  # 一般来说局部变量会随着函数的调用生成,函数调用结束而释放

闭包会延长其捕获变量的生命周期,即使创建它的函数已经执行完毕
cell 对象是 Python 实现闭包的核心机制,用于保存闭包捕获的外部变量

def outer(x):a = 300def inner():print(x + a)return innerf1 = outer(100)
print(f1.__name__)	# inner
f1()	# 400
print(dir(outer))   # 包含 '__closure__'
print(f1.__closure__)	# 查看闭包捕获的 cell 对象
print(f1.__closure__[0].cell_contents)	# 获取第一个 cell 对象的值:300

二、装饰器

装饰器是一种程序设计模式,在不改变函数或类的源代码的基础上,添加额外功能
装饰器的本质是闭包函数,它需要把一个callable(__call__)对象作为参数传入

只能装饰函数或类
可以使用函数实现装饰器,也可以使用类实现装饰器

1. 编辑统计运行时间装饰器

import time
def runtime(func):def inner(*args, **kwargs): # *args接收可变长位置参数 **kwargs接收可变长的关键字参数    让装饰器更加通用start = time.time()	# 当前时间戳(1970年1月1日00:00开始到现在的秒数)result = func(*args, **kwargs)  # 将参数传递给原函数end = time.time()print(f"执行函数{func.__name__}花费了{end - start}s")return resultreturn inner# @修饰符  语法糖
@runtime    # add = runtime(add)    被装饰过后的变量就不是指向原函数了
def add(a, b):time.sleep(1)return a+bprint(add.__name__) # inner
result = add(1, 2)
print(result)

2. 编辑日志记录装饰器

日志 记录程序运行过程中发生的事情

  1. 排错,调试 分析故障定位
  2. 用户行为分析

记录日志的模块 logging --> 五个等级

日志等级数值表示描述
DEBUG10最详细的日志信息,开发过程中用于诊断问题
INFO20详细程度仅次于debug,记录关键节点信息
WARNING30(默认)当前有不期望的事情发生,提出警告
ERROR40发生了错误问题,导致某些功能不能使用了
CRITICAL50发生了严重错误导致程序不能继续执行
import logginglogging.debug("this is a debug message")
logging.info("this is an info message")
logging.warning("this is a warning message")
logging.error("this is an error message")
logging.critical("this is a critical message")

logging 日志模块 四大组件

四大组件作用
日志器 (logger)用于记录日志的接口
处理器 (handler)用于指明日志输出到哪里
格式器 (formatter)指明日志格式
过滤器 (filter)用于日志过滤
import logging# 创建一个日志器,用于记录日志
logger = logging.getLogger()# 处理器,记录到文件
fh = logging.FileHandler('test.log')    # 输出到文件
ch = logging.StreamHandler()    # 输出到屏幕
"""
# 日志轮转
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
# 按时间
fh2 = TimedRotatingFileHandler(filename='test.log', when='D', interval=1, encoding='utf-8',backupCount=1)
# 按大小
fh3 = RotatingFileHandler('sc.log', maxBytes=1000000, backupCount=1)
"""
# 格式器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')# 将格式器绑定到文件处理器
fh.setFormatter(formatter)# 将处理器绑定到日志器
logger.addHandler(fh)
logger.addHandler(ch)# 日志器记录文件日志
logger.setLevel(logging.DEBUG)  # 设置日志等级
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')

写一个日志记录的装饰器,每执行一个函数,记录一下

日志格式:什么时间点,什么文件,多少行代码
日志内容:记录执行哪个函数

import logginglogger = logging.getLogger()
fh = logging.FileHandler('test.log')
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(filename)s - %(lineno)d - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(ch)
logger.setLevel(logging.DEBUG)
def log(func):def inner(*args, **kwargs):logger.debug(f"执行了函数 {func.__name__}")result = func(*args, **kwargs)return resultreturn inner
# 装饰器可以叠加使用
@log
@runtime
def add(a, b):return a+bresult = add(1, 2)

test.log 文件效果

2025-07-21 16:53:12,165 - 02.装饰器.py - 71 - 执行了函数 add


三、保留元信息

装饰器可能会掩盖原函数的元信息(如 __name____doc__)。使用 functools.wraps 可以保留这些信息

from functools import wraps
def runtime(func):@wraps(func)    # 将func的函数元数据 赋值给被装饰的函数def inner(*args, **kwargs):     # *args接收可变长位置参数 **kwargs接收可变长的关键字参数    让装饰器更加通用start = time.time()result = func(*args, **kwargs)  # 将参数传递给原函数end = time.time()print(f"执行函数{func.__name__}花费了{end - start}s")return resultreturn inner@runtime    # add = runtime(add)    被装饰过后的变量就不是指向原函数了
def add(a, b):time.sleep(1)return a+bprint(add.__name__) # add

四、带参数的装饰器

自身不传入参数的装饰器,使用两层函数定义
自身传入参数的装饰器,使用三层函数定义

import logging
# 创建日志器
logger = logging.getLogger()
fh = logging.FileHandler('test.log')
formatter = logging.Formatter('%(asctime)s - %(filename)s - %(lineno)d - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.setLevel(logging.DEBUG)def deco(path):def log(func):def inner(*args, **kwargs):logger.debug(f"执行了函数 {func.__name__}, 记录在日志文件{path}中")result = func(*args, **kwargs)return resultreturn innerreturn log@deco(path="sc.log")
# log = deco(path = "sc.log")   # 先运行外层函数 得到一个装饰器
# add = log(add)       # 再使用这个装饰器 去装饰add函数
def add(a, b):return a+badd(1,2)

带参数的登录判断装饰器

def login(name, passwd):def run(func):def inner(*args, **kwargs):if name == "root" and passwd == "123456":print("登录成功")result = func(*args, **kwargs)return resultelse:print("登录失败")return innerreturn runname, passwd = input("请输入用户名和密码:").split()
@login(name, passwd)
def add(a, b):return a+badd(1,2)
# 请输入用户名和密码:root 123456
# 登录成功

五、用类实现装饰器

用类实现 统计函数执行时间的装饰器

import time
class A:def __init__(self,func):self.func = funcdef __call__(self, *args, **kwargs):start = time.time()result = self.func(*args, **kwargs)end = time.time()print(f"执行函数{self.func.__name__}花费了{end - start}s")return result@A      # add = A(add)
def add(a,b):time.sleep(1)return a + bresult = add(1,2)

用类实现 带参数的装饰器

class A:def __init__(self, name, passwd):self.name = nameself.passwd = passwddef __call__(self, func):def inner(*args, **kwargs):if self.name == "root" and self.passwd == "123456":print("登录成功")start = time.time()result = func(*args, **kwargs)end = time.time()print(f"执行函数{func.__name__}花费了{end - start}s")return resultelse:print("登录失败")return innername, passwd = input("请输入用户名和密码:").split()
@A(name, passwd)  # a=A('sc')  add = a(add)
def add(a, b):time.sleep(1)return a + badd(1, 2)
# 请输入用户名和密码:root 123456
# 登录成功
# 执行函数add花费了1.0007741451263428s

总结

  1. 函数即对象
    在 Python 中,函数可以被赋值给变量、作为参数传递、甚至在其他函数内部定义
  2. 闭包(Closure)
    函数可以捕获并记住其所在的封闭命名空间中的变量,即使该命名空间已经执行完毕
  3. 语法糖
    @decorator 语法等价于 func = decorator(func)
http://www.dtcms.com/a/301407.html

相关文章:

  • 【ST表、倍增】P7167 [eJOI 2020] Fountain (Day1)
  • QT6 源,七章对话框与多窗体(15)多文档 MDI 窗体 QMdiArea 篇一:属性,公共成员函数,信号与槽函数
  • 多智能体架构
  • 《计算机组成原理与汇编语言程序设计》实验报告四 Debug及指令测试
  • setnonblocking函数用途和使用案例
  • 在本地环境中运行 ‘dom-distiller‘ GitHub 库的完整指南
  • OSPF路由协议 多区域
  • 【ESP32】无法找到: “${env:IDF_PATH}/components/“的路径报错问题以及CMAKE构建不成功问题
  • Cursor报错解决【持续更新中】
  • 金融科技中的远程开户、海外个人客户在线开户、企业客户远程开户
  • 深入解析Java运行机制与JVM内存模型
  • 【Web APIs】JavaScript 节点操作 ⑩ ( 节点操作综合案例 - 动态生成表格案例 )
  • windows 11 JDK11安装
  • LeetCode 239:滑动窗口最大值
  • 五自由度磁悬浮轴承转子不平衡振动抑制破局:不平衡前馈补偿+自抗扰控制实战解析
  • MySQL 全详解:从入门到精通的实战指南
  • 第二阶段-第二章—8天Python从入门到精通【itheima】-138节(MySQL的综合案例)
  • 设备分配与回收
  • 数据处理实战(含代码)
  • OpenFeign-远程调用((Feign的使用方法))
  • Spring Boot 配置文件常用配置属性详解(application.properties / application.yml)
  • 【PCIe 总线及设备入门学习专栏 5.3.4 -- PCIe PHY Firmware 固件加载流程】
  • 如何思考一个动态规划问题需要几个状态?
  • [每周一更]-(第150期):AI Agents:从概念到实践的智能体时代
  • net8.0一键创建支持(Elastic)
  • 2025C卷 - 华为OD机试七日集训第1期 - 按算法分类,由易到难,循序渐进,玩转OD
  • Spring 容器注入时查找 Bean 的完整规则
  • Flutter中 Provider 的基础用法超详细讲解(二)之ChangeNotifierProvider
  • 力扣热题100----------53最大子数组和
  • 咨询进阶——解读40页公司战略解码方法【附全文阅读】