Python 装饰器是一种特殊的语法结构,它可以在不修改原函数代码的情况下,动态地增加某些功能或者修改函数的行为。在实际编程中,装饰器被广泛应用于日志记录、性能分析、权限验证等方面。下面我将为您详细讲解 Python 装饰器的使用方法,以及两个示例说明。
一、装饰器基础
1.1 简单的装饰器
我们先看一个简单的装饰器示例,它可以在函数执行前后打印出函数名称和执行时间:
import time
def time_it(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print('{} took {} seconds to run.'.format(func.__name__, end - start))
return result
return wrapper
@time_it
def my_function():
time.sleep(1)
print('Function executed.')
my_function()
上述代码中,我们定义了一个 time_it
装饰器,它是一个闭包函数,接受一个函数作为参数,并返回一个新的函数 wrapper
。在新的函数中,我们记录了函数执行前的时间戳 start
,执行了原函数 func
(包含 *args
和 **kwargs
参数),并记录了函数执行后的时间戳 end
。最后,我们打印出函数名称和执行时间,并返回函数执行结果。
通过在 my_function
函数上加上 @time_it
装饰器语法糖,我们就成功地将 my_function
函数传递给 time_it
函数进行装饰,使得在函数执行前后打印出了执行时间。
1.2 带参数的装饰器
除了上面的示例之外,我们还可以定义带参数的装饰器,具体表示为:
import time
def logger(prefix):
def wrapper(func):
def inner_wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print('{}: {} took {} seconds to run.'.format(prefix, func.__name__, end - start))
return result
return inner_wrapper
return wrapper
@logger('Test')
def my_function():
time.sleep(1)
print('Function executed.')
my_function()
上述代码中,我们定义了一个 logger
装饰器,并且这个装饰器接受一个 prefix
参数。在 logger
函数内部,我们再次定义了一个闭包函数 wrapper
,接收一个函数 func
作为参数。在 wrapper
函数内部,我们定义了另一个闭包函数 inner_wrapper
,并对其进行了类似的封装。此时,我们不仅能够在函数执行前后打印出函数名称和执行时间,还能够增加一个前缀用以区分日志来源。
最后,我们在 my_function
函数上加上 @logger('Test')
装饰器语法糖,使得 my_function
函数包含了日志记录的功能,并且日志前缀设定为 ‘Test’。
二、装饰器高级应用
2.1 类装饰器
除了函数以外,我们也可以使用类作为装饰器。假设我们要统计某个类中所有函数的执行时间,我们可以定义一个 TimeIt
类装饰器:
import time
class TimeIt:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
start = time.time()
result = self.func(*args, **kwargs)
end = time.time()
print('{} took {} seconds to run.'.format(self.func.__name__, end - start))
return result
@TimeIt
def my_function():
time.sleep(1)
print('Function executed.')
my_function()
上述代码中,我们定义了一个 TimeIt
类,它接收一个函数作为参数,并定义了 __call__
方法,使得该对象能够像函数一样进行调用。在调用时,我们记录了函数执行前的时间戳 start
,执行了原函数 self.func
(包含 *args
和 **kwargs
参数),并记录了函数执行后的时间戳 end
。最后,我们打印出函数名称和执行时间,并返回函数执行结果。
通过在 my_function
函数上加上 @TimeIt
装饰器语法糖,我们就成功地将 my_function
函数传递给 TimeIt
类进行装饰,使得在函数执行前后打印出了执行时间。
2.2 堆栈式装饰器
另一个有趣的例子是实现堆栈式装饰器,这种装饰器可以对函数进行嵌套调用,并在函数返回时返回调用堆栈。下面是一个示例代码:
def stack_trace(func):
def wrapper(*args, **kwargs):
wrapper.calls.append(func.__name__)
return func(*args, **kwargs)
wrapper.calls = []
return wrapper
@stack_trace
def func_1():
print('calling func 1')
func_2()
@stack_trace
def func_2():
print('calling func 2')
func_1()
print(func_1.calls)
上述代码中,我们定义了一个 stack_trace
装饰器,它接收一个函数 func
作为参数,并返回一个新的函数 wrapper
。在新的函数中,我们使用了 wrapper.calls
作为一个列表变量记录了函数的调用堆栈。最后,我们返回了带有堆栈信息的装饰器。
在 func_1
和 func_2
函数上,我们分别使用了 @stack_trace
装饰器语法糖,使得在函数调用时,函数的调用堆栈会逐步加入到 wrapper.calls
列表中。最后,我们打印出了 func_1
的调用堆栈信息。