1. 简介
装饰器是Python中非常强大的一个特性,它是一种将已有函数进行封装的技术。在不改变原函数的情况下,通过装饰器可以为函数添加新的功能。装饰器本质上是一个函数,它可以接收一个函数作为参数,返回一个经过包装后的函数,这个包装后的函数通常也可以接收函数的所有参数,并在此基础上完成新增的功能。
2. 基础语法
在Python中,装饰器是通过使用'@'语法糖来实现的,具体如下:
@decorator
def func():
pass
装饰器可以理解为一个函数的装饰物,它会将函数包装在自己的内部,从而实现对函数进行增强或者改变函数的行为。在上述代码中,'@decorator'就是一个装饰器,它需要一个函数作为参数,并返回一个函数。这里需要注意的是,装饰器必须返回一个函数,否则程序会报错。
3. 使用场景
3.1 计时器
在实际开发中,经常需要统计程序在执行时的时间,而此时可以使用装饰器来实现。下面的代码展示了如何通过装饰器实现一个简单的计时器。
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('time used:', end_time - start_time)
return res
return wrapper
@timer
def test_timer(n):
res = 0
for i in range(n):
res += i
return res
test_timer(10000000)
在上面的代码中,我们定义了一个装饰器'@timer',它将会为函数'test_timer'添加计时器功能,计算程序从开始到结束的时间并输出。最后调用'test_timer'函数,并传入参数'10000000'。
3.2 缓存
有时候,在处理一些计算密集型的任务时,可能会重复运行多次相同的函数。这时,我们可以使用装饰器来实现缓存,并将已经计算过的结果保存下来,避免重复计算。
def memoize(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
res = func(*args)
cache[args] = res
return res
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(35))
在上面的代码中,我们定义了一个装饰器'memoize',它接受一个参数'func',返回一个包装函数'wrapper'。在'wrapper'函数中,我们通过一个字典来保存调用时的参数和结果,如果需要重新计算时,先查看字典中是否保存了该参数的结果,如果已经有了就直接返回结果,否则进行计算,并将结果保存到字典中。
3.3 日志系统
日志是一种非常重要的功能,可以记录系统运行时的事件和状态,方便排错和分析。使用装饰器可以非常方便地实现日志功能,下面的代码演示了如何使用装饰器为函数添加日志记录功能。
def logger(func):
def wrapper(*args, **kwargs):
print('arguments:', args, kwargs)
res = func(*args, **kwargs)
print('result:', res)
return res
return wrapper
@logger
def test_logger(a, b, c):
return a + b + c
print(test_logger(1, 2, c=3))
在上面的代码中,我们定义了一个装饰器'logger',它接收一个函数作为参数,并返回一个包装函数'wrapper'。在'wrapper'函数中,我们先输出函数调用时的参数,然后执行原来的函数,并将结果输出。
4. 多个装饰器的使用
在实际使用中,一个函数可能需要添加多个装饰器,例如需要添加计时器、缓存和日志功能。这时,我们可以使用多个装饰器进行组合。下面的代码演示了如何使用多个装饰器进行组合。
import time
def memoize(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
res = func(*args)
cache[args] = res
return res
return wrapper
def logger(func):
def wrapper(*args, **kwargs):
print('arguments:', args, kwargs)
res = func(*args, **kwargs)
print('result:', res)
return res
return wrapper
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('time used:', end_time - start_time)
return res
return wrapper
@logger
@memoize
@timer
def test(n):
res = 0
for i in range(n):
res += i
return res
test(10000000)
在上面的代码中,我们为函数'test'添加了三个装饰器,分别是'@logger'、'@memoize'和'@timer'。它们的执行顺序是由下往上的,因此先执行'timer'装饰器,然后执行'memoize'装饰器,最后执行'logger'装饰器。
5. 相关注意事项
5.1 不修改原函数参数和返回值
在编写装饰器时,需要保证不修改原函数的参数和返回值。因为如果在装饰器中修改这些值,就会影响到原函数的使用。
5.2 保留原函数的元信息
在使用装饰器时,需要保留原函数的元信息,例如函数名和文档字符串等。这可以通过使用Python内置的@functools.wraps来实现。
import functools
def logger(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('arguments:', args, kwargs)
res = func(*args, **kwargs)
print('result:', res)
return res
return wrapper
@logger
def test_logger(a, b, c):
'''This is a test function'''
return a + b + c
print(test_logger.__name__)
print(test_logger.__doc__)
在上面的代码中,我们在装饰函数'wrapper'前使用了@functools.wraps(func)语法糖。这可以使得装饰器内部的函数'wrapper'保留原函数的元信息,例如函数名和文档字符串。
5.3 装饰器带参数
除了可以使用单个装饰器,还可以为装饰器传递参数,这样可以在不同的场景下使用不同的参数进行装饰。下面的代码演示了如何使用带参数的装饰器。
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(n):
res = func(*args, **kwargs)
return res
return wrapper
return decorator
@repeat(3)
def test_repeat(a, b):
return a + b
print(test_repeat(3, 4))
在上面的代码中,我们定义了一个带参数的装饰器'repeat',它接收一个参数'n',返回一个装饰器'decorator'。在'decorator'装饰器中,我们定义了一个新的函数'wrapper',并在其中通过循环来重复调用原函数。然后返回结果。
5.4 将装饰器定义为类
使用类来定义装饰器,可以使装饰器更加灵活和可扩展。
class Decorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('arguments:', args, kwargs)
res = self.func(*args, **kwargs)
print('result:', res)
return res
@Decorator
def test_decorator(a, b, c):
'''This is a test function'''
return a + b + c
print(test_decorator(1, 2, c=3))
在上面的代码中,我们定义了一个名为'Decorator'的类,它包含了初始化方法'__init__'和特殊方法'__call__'。'__init__'方法接收一个函数作为参数,并保存到实例变量'func'中。'__call__'方法是当实例被调用时,会自动触发,其中我们通过'print'语句输出函数调用时的参数,并执行原函数,最后返回结果。
6. 总结
装饰器是Python中非常强大的一种特性,它可以为函数添加新的功能,且不允许修改原函数的行为。在实际开发中,我们可以使用装饰器来实现很多功能,例如计时器、缓存和日志记录等。此外,我们还可以为装饰器传递参数,实现在不同的场景下使用不同的参数进行装饰。除此之外,我们还可以通过将装饰器定义为类的方式,使其更加灵活和可扩展。希望本文可以帮助大家更好地理解和应用Python中的装饰器。