装饰器,这可是Python开发中绕不开的经典话题,不论你是写代码的老手,还是刚入行的萌新,都得和它打上几轮交道。而记录函数执行时间这个功能,更是装饰器中的“常客”。
今天我就带大家来全面解锁一下这块儿的知识,先从最基础的实现聊起,然后逐步深入,最终给出面试中的“加分”回答。坐稳了,开始上代码!
先聊聊装饰器是干啥的。简单说,装饰器就是一个“增强器”,能在不改动原始函数代码的情况下,为函数加上一些“外挂技能”,比如记录函数执行时间、打印日志、检查权限等等。
这里我们要完成的是统计一个函数的执行时间,便于分析代码性能、找到瓶颈。咱们一步步走,从函数装饰器讲起,然后再聊类装饰器和可参数化的装饰器。
先来说说最简单的实现,咱们可以用一个函数来实现记录函数执行时间的装饰器。装饰器的核心思想就是在不改动原有函数的前提下,给它添加额外的功能。
通常我们通过装饰器为一个函数增加一些处理逻辑,而不是直接改动函数本身。这个装饰器会在函数执行之前记录一下开始时间,函数执行完后计算并输出它的执行时间。
from functools import wraps
from time import time
def record_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time() # 记录开始时间
result = func(*args, **kwargs) # 执行原函数
print(f'{func.__name__} 执行时间: {time() - start}秒') # 输出执行时间
return result # 返回原函数的返回值
return wrapper
在这个代码里,我们用到了 wraps
装饰器,目的就是保留原函数的元数据,比如函数名和文档字符串等,不然 record_time
装饰过的函数在外部调用时会丢失掉它的元信息。record_time
装饰器很通俗易懂——先记录开始时间,运行函数,最后计算并输出执行时间。
装饰一个函数的步骤也很简单,直接在目标函数前加上 @record_time
即可:
@record_time
def example_function():
# 模拟耗时操作
for _ in range(1000000):
pass
example_function()
运行这个代码,终端里就能看到 example_function
的执行时间。
这只是装饰器的一种实现方式。我们还可以用类来实现装饰器。之所以这样做,是因为类装饰器提供了更高的灵活性。
通过 __call__
方法,类的实例可以像函数一样被调用,从而实现和函数装饰器类似的效果。很多情况下,类装饰器能更方便地管理状态。来看下面这个例子:
from functools import wraps
from time import time
class Record:
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time() # 记录开始时间
result = func(*args, **kwargs) # 执行原函数
print(f'{func.__name__} 执行时间: {time() - start}秒') # 输出执行时间
return result
return wrapper
使用这个类装饰器和函数装饰器一样简单:
@Record()
def example_function():
for _ in range(1000000):
pass
example_function()
这个类装饰器的优点之一是可以轻松管理多个属性和复杂状态。比如说我们想在装饰器中加入一些配置选项,或者记录函数被调用的次数,用类实现会更简洁清晰。
说到装饰器,就不得不提参数化装饰器。这类装饰器允许我们传入参数,根据不同的需求控制装饰器的行为。
比方说,如果我们不想直接在终端中输出函数的执行时间,而是希望调用者自定义执行时间的输出方式,这种情况就可以用参数化装饰器来实现。
from functools import wraps
from time import time
def record_time(output):
"""可以参数化的装饰器"""
def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time() # 记录开始时间
result = func(*args, **kwargs) # 执行原函数
output(func.__name__, time() - start) # 通过参数控制输出
return result
return wrapper
return decorate
假设我们有一个自定义输出函数:
def custom_output(func_name, exec_time):
print(f"Function {func_name} took {exec_time:.2f} seconds to execute.")
使用的时候直接传入 custom_output
函数:
@record_time(custom_output)
def example_function():
for _ in range(1000000):
pass
example_function()
运行这段代码后,会看到 custom_output
控制输出格式,这样就完全实现了执行时间的自定义输出。
到这里,我们已经看了三种不同类型的记录执行时间装饰器:函数装饰器、类装饰器、参数化装饰器。在实际开发中,灵活选用合适的装饰器能显著提升代码的复用性和可读性。
如果在面试中被问到如何实现一个记录执行时间的装饰器,回答这个问题的关键在于要展示你对装饰器基本概念的理解,最好能清晰地解释每种实现方式的优势。我们可以这么回答:
装饰器是Python中一种实现增强功能的工具,通过装饰器可以在不改变原函数的情况下添加新的功能。我来举一个简单例子,通常记录执行时间的装饰器可以通过函数或类来实现。
函数装饰器实现起来简单直观,但类装饰器提供了更强的灵活性,尤其适合需要维护内部状态或配置的场景。此外,如果有需要自定义输出方式的需求,我会使用参数化装饰器,这样可以传入不同的输出函数,灵活控制输出内容和格式。比如,可以用它将执行时间记录到文件或发送到监控系统。
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。