Python函数进阶技巧:使用装饰器在函数执行前后添加额外逻辑
装饰器是 Python 中常用的一种高阶函数,可以在不改变原代码的情况下给函数增加功能。装饰器通常用于在函数执行前后添加额外的逻辑,比如记录日志、验证参数、缓存结果等。
在介绍装饰器之前,先了解一下 Python 中的函数对象。在 Python 中,函数也是一种对象,可以当做参数传入函数,也可以当做函数的返回值返回。这就是 Python 中的“一切皆对象”的思想。因为函数是一种对象,所以我们可以对其进行操作,比如:
def hello():
print('hello')
# 给函数对象添加属性
hello.name = 'hello'
print(hello.name) # 输出 'hello'
当然,这种操作并不常见。常见的操作是将函数作为参数传入另一个函数中,并在这个函数中对其进行修改、扩展等操作。这就是装饰器的基本思想。
下面我们来对一个函数进行装饰器的操作。我们假设有一个函数 print_time,用于输出当前时间。现在我们想要在这个函数的执行前后分别输出一些信息,可以通过装饰器实现:
import time
def before_after(func):
def wrapper():
print('before')
func()
print('after')
return wrapper
@before_after
def print_time():
print(time.strftime('%Y-%m-%d %H:%M:%S'))
print_time()
上述代码定义了一个名为 before_after 的函数,用于接收另一个函数作为参数,并返回一个函数对象。我们将另一个函数 print_time 传入 before_after 中,并在前后分别输出信息。实现过程中,我们定义了一个名为 wrapper 的函数,它使用了闭包的方式来保存传入的函数对象,以便后续调用。在最后,我们使用 @before_after 的语法糖来装饰 print_time 函数。
输出结果如下:
before 2021-11-08 09:53:45 after
可以看到,执行 print_time 函数时,before_after 装饰器添加了额外的逻辑,输出了 before 和 after。这是一种非常常见的装饰器用法,比如在函数执行前后记录一些信息、计算函数执行时间等。下面我们再给出几个常见的装饰器示例。
### 1. 计算函数执行时间
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f'{func.__name__} executed in {end_time-start_time}s')
return result
return wrapper
@timer
def expensive_calculation(n):
return sum(range(n))
expensive_calculation(1000000)
上述代码定义了一个名为 timer 的装饰器,用于计算函数的执行时间。在 wrapper 函数中,我们记录函数执行前后的时间,并输出执行时间。在最后,我们使用 @timer 的语法糖来装饰 expensive_calculation 函数。
输出结果如下:
expensive_calculation executed in 0.027530908584594727s
可以看到,执行 expensive_calculation 函数时,timer 装饰器添加了额外的逻辑,计算了函数执行时间,并输出了执行时间。
### 2. 缓存函数结果
def cache(func):
cache = {}
def wrapper(*args, **kwargs):
key = args + tuple(sorted(kwargs.items()))
if key in cache:
print(f'cache hit: {key}')
return cache[key]
else:
result = func(*args, **kwargs)
cache[key] = result
return result
return wrapper
@cache
def slow_function(n):
time.sleep(1)
return n ** 2
slow_function(3)
slow_function(3)
上述代码定义了一个名为 cache 的装饰器,用于缓存函数的执行结果。在 wrapper 函数中,我们先检查函数的参数是否已经在缓存中,如果存在则直接返回结果,否则执行函数并将结果保存在缓存中。在最后,我们使用 @cache 的语法糖来装饰 slow_function 函数。
输出结果如下:
3 cache hit: (3,)
可以看到, 次执行 slow_function 函数时,经过了 1s 的计算,输出了计算结果 9。第二次执行时,cache 装饰器缓存了计算过程,并输出了 cache hit: (3,),表示直接从缓存中取出了结果。
装饰器是 Python 中非常强大的一种编程技巧,可以用于实现很多有用的功能。除了上述示例中的计算执行时间、缓存结果等功能外,还可以用装饰器来实现日志记录、性能统计、权限检查、错误处理等功能。在实际工作中,我们可以根据需求编写自己的装饰器,提高代码的可重用性和可维护性。
