一、Python装饰器概览
Python装饰器是Python编程中一个非常强大的概念,它提供了一种灵活的方式来修改或扩展函数或类的行为。装饰器常用于添加或修改函数或方法的功能,比如缓存、计时器、日志记录等。装饰器也大大简化了代码重复度,增强了代码的可读性和可维护性。
1.1 装饰器的定义
装饰器(Decorator)是一种很简单但是非常重要的设计模式。它可以动态地给一个对象添加一些额外的功能,而不会修改这个对象的源代码。
1.2 装饰器的作用
Python装饰器主要用于以下方面:
- 动态地给某个函数或方法添加功能,而不改变其原有的定义和调用方式;
- 减少重复的代码,增强代码可读性和可维护性。
1.3 装饰器的特点
Python装饰器有以下几个特点:
- 装饰器本身就是一个函数或类;
- 装饰器可以接受其他的函数或类作为参数;
- 装饰器还可以返回一个函数或类作为输出。
1.4 装饰器的语法
Python装饰器的语法格式如下:
@decorator
def function():
pass
上面的代码中,@decorator是一个装饰器,可以接受一个函数作为参数,并返回另外一个函数。函数function被装饰器修饰后,会在调用时自动添加上一些额外的功能。
1.5 装饰器的分类
Python装饰器一般分为以下几类:
- 类装饰器
- 函数装饰器
- 参数化装饰器
二、Python装饰器实例
下面我们通过实例来了解Python装饰器的具体使用。
首先我们定义一个装饰器log,它可以在函数调用前后打印出函数的名称和参数:
def log(func):
def wrapper(*args, **kw):
print('调用函数 %s():' % func.__name__)
func(*args, **kw)
print('调用结束!')
return wrapper
在定义了log装饰器后,我们可以使用@log来使用它:
@log
def now():
print('2022-3-2')
我们来分析一下这段代码的实现过程。在使用@log来修饰函数now()时,Python解释器会自动把下面的语句:
now = log(now)
转换成这样的语句:
now = log(now)
也就是说,now函数会被包装成wrapper函数。当我们调用now()函数时,实际上执行的是wrapper()函数,wrapper()函数会在调用now()函数前后分别打印出调用信息。
三、参数化装饰器
除了定义简单的装饰器外,我们还可以定义带参数的装饰器。带参数的装饰器主要用于为不同的对象添加不同的功能。
def log(prefix):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (prefix, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
上述代码中,定义了一个装饰器log,它可以指定打印的前缀信息。在使用这个带参数的装饰器时,需要使用两级嵌套的装饰器。
首先需要使用log('execute')来创建一个装饰器,然后使用这个装饰器来修饰函数:
@log('execute')
def now():
print('2022-3-2')
这时调用now()函数,会在控制台输出执行信息:
execute now():
2022-3-2
四、实战练习
下面我们来看一个实战练习,练习内容是编写一个能够缓存函数结果的装饰器。
我们可以定义一个带参数的缓存装饰器,它会在内存中缓存函数的结果,如果下次再次调用函数时,会直接返回结果,而不是重新计算。缓存的数据可以定期清除,避免占用过多内存。
import time
from functools import wraps
def cache(seconds):
def _cache(func):
@wraps(func)
def wrapper(*args, **kwargs):
key = str(args) + str(kwargs)
if cache.has_key(key) and (time.time() - cache[key]['time']) < seconds:
print('hit cache')
return cache[key]['result']
result = func(*args, **kwargs)
cache[key] = {'time': time.time(), 'result': result}
return result
return wrapper
return _cache
cache.has_key = lambda key: key in cache
@cache(seconds=5)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print(fib(8))
print(fib(8))
time.sleep(6)
print(fib(8))
print(fib(8))
上述代码中,使用了Python内置的functools.wraps装饰器来保持被修饰函数的元信息(如函数名、参数、注释等)。当函数被重新赋值后,这些元信息会丢失。使用wraps装饰器来修饰wrapper函数,可以解决该问题。
五、总结
Python装饰器是Python编程中一个重要的概念,它可以动态地给一个对象添加一些额外的功能,而不需要修改这个对象的源代码。Python装饰器有非常灵活的应用场景,可以用于函数和类的定义、参数检查、性能优化等方面。在实际应用中,建议掌握不同类型的装饰器(如类装饰器、函数装饰器、参数化装饰器)以及装饰器的用法和语法规则,这会大大提高Python编程的效率和可读性。