python-装饰器
python-装饰器
- 一、闭包
- 二、装饰器
- 1. 编辑统计运行时间装饰器
- 2. 编辑日志记录装饰器
- 三、保留元信息
- 四、带参数的装饰器
- 五、用类实现装饰器
- 总结
一、闭包
闭包满足的条件:
- 必须要有内外函数
- 内函数必须引用外函数变量
- 外函数必须返回内函数
变量的生命周期
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. 编辑日志记录装饰器
日志 记录程序运行过程中发生的事情
- 排错,调试 分析故障定位
- 用户行为分析
记录日志的模块 logging
--> 五个等级
日志等级 | 数值表示 | 描述 |
---|---|---|
DEBUG | 10 | 最详细的日志信息,开发过程中用于诊断问题 |
INFO | 20 | 详细程度仅次于debug,记录关键节点信息 |
WARNING | 30 | (默认)当前有不期望的事情发生,提出警告 |
ERROR | 40 | 发生了错误问题,导致某些功能不能使用了 |
CRITICAL | 50 | 发生了严重错误导致程序不能继续执行 |
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
总结
- 函数即对象
在 Python 中,函数可以被赋值给变量、作为参数传递、甚至在其他函数内部定义 - 闭包(Closure)
函数可以捕获并记住其所在的封闭命名空间中的变量,即使该命名空间已经执行完毕 - 语法糖
@decorator 语法等价于 func = decorator(func)