在Python函数中使用装饰器实现函数扩展
Python装饰器是一种很有用的编程技巧,可以在不改变原函数的代码的情况下,添加或修改其功能。装饰器可以用于函数、类、方法等对象上,本文将着重介绍在Python函数中使用装饰器实现扩展的方法。
一、什么是装饰器
装饰器(Decorator)是 Python 语言中的一种特殊语法,它可以在代码运行期间动态地修改函数或类。装饰器常用于扩展函数的功能,例如:添加日志、鉴权、计时等操作,而不需要修改原函数的代码。
装饰器本质上是一个 Python 函数或类,它接受一个函数或类作为参数,并返回一个新的函数或类。使用装饰器时,需要在原函数的定义上面添加 @decorator_name,这样 Python 解释器就会自动执行装饰器函数,并将原函数作为参数传递进去,最终返回一个新函数作为原函数的代理对象。
下面是一个简单的装饰器示例:
def my_decorator(func):
def wrapper():
print("Start.")
func()
print("End.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
代码说明:
定义了一个装饰器函数 my_decorator,它接受一个函数作为参数,并返回一个新函数 wrapper。
新函数 wrapper 在原函数执行前后添加了一些额外的操作,打印了“Start.”和“End.”。
在原函数 say_hello 上方添加了装饰器 @my_decorator,实际上是将其传递给 my_decorator 函数,返回一个新函数作为 say_hello 的代理对象。
最终,执行 say_hello() 时,实际上执行的是新函数 wrapper。输出结果为:
Start. Hello! End.
二、装饰器类型
根据装饰器的参数类型和返回类型,可以将装饰器分为函数装饰器和类装饰器。函数装饰器是最常见的一种,它接受一个函数作为参数,并返回一个新函数;类装饰器则接受一个类作为参数,并返回一个新的类。
函数装饰器示例:
def my_decorator(func):
def wrapper():
print("Start.")
func()
print("End.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
类装饰器示例:
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self):
print("Start.")
self.func()
print("End.")
@MyDecorator
def say_hello():
print("Hello!")
say_hello()
代码说明:
类装饰器 MyDecorator 接受一个函数作为参数,并定义了 __call__ 方法,在该方法中添加了额外的操作,然后调用原函数。
在原函数 say_hello 上方添加了装饰器 @MyDecorator,实际上是将其传递给 MyDecorator 类,返回一个新的代理类。代理类的 __call__ 方法调用原函数并加上额外的操作。
最终,执行 say_hello() 时,实际上执行的是代理类的实例对象。输出结果同上。
三、装饰器的应用
装饰器可以用于函数的某些扩展功能,例如:添加日志、计时、缓存结果,以及权限控制等功能。下面将分别介绍这些应用场景的实现方法。
1、添加日志
在函数执行前后添加日志信息,可以帮助我们更好地了解函数的运行状况,便于分析问题和优化性能。下面是一个简单的添加日志的装饰器实现:
import logging
def log(func):
def wrapper(*args, **kwargs):
logging.basicConfig(filename=f"{func.__name__}.log", level=logging.INFO)
logging.info(f"{func.__name__} started with args: {args}, kwargs: {kwargs}")
result = func(*args, **kwargs)
logging.info(f"{func.__name__} ended with result: {result}")
return result
return wrapper
@log
def add(a, b):
return a + b
print(add(2, 3))
代码说明:
定义了装饰器 log,它接受一个函数作为参数,并定义了新函数 wrapper,在该函数中添加了日志信息。
在原函数 add 上方添加装饰器 @log,实际上是将其传递给 log 函数,返回一个新函数作为原函数的代理对象。
最终,执行 add(2, 3) 时,实际上执行的是新函数 wrapper,输出结果为 5。同时,在当前目录下生成一个名为 add.log 的日志文件,其中包含了函数执行前后的日志信息。
2、添加计时器
在函数执行前后添加计时器,可以帮助我们了解函数的运行效率,便于优化性能。下面是一个简单的添加计时器的装饰器实现:
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__} took {end_time - start_time:.3f}s to run.")
return result
return wrapper
@timer
def add(a, b):
time.sleep(1)
return a + b
print(add(2, 3))
代码说明:
定义了装饰器 timer,它接受一个函数作为参数,并定义了新函数 wrapper,在该函数中添加了计时器。
在原函数 add 上方添加装饰器 @timer,实际上是将其传递给 timer 函数,返回一个新函数作为原函数的代理对象。
最终,执行 add(2, 3) 时,实际上执行的是新函数 wrapper,输出结果为 5。同时,输出计时器信息,如:add took 1.001s to run。
3、缓存结果
如果一个函数的计算结果是稳定的,那么可以使用缓存技术来避免重复计算,提高函数的执行速度。下面是一个简单的缓存结果的装饰器实现:
def cache(func):
cached_results = {}
def wrapper(*args):
if args in cached_results:
return cached_results[args]
else:
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
print(fibonacci(20))
代码说明:
定义了装饰器 cache,它接受一个函数作为参数,并定义了新函数 wrapper,在该函数中添加了缓存技术。
在原函数 fibonacci 上方添加装饰器 @cache,实际上是将其传递给 cache 函数,返回一个新函数作为原函数的代理对象。
最终,执行 fibonacci(10) 和 fibonacci(20) 时,实际上执行的是新函数 wrapper,输出结果分别为 55 和 6765。同时,第二次执行 fibonacci(20) 时,实际上从缓存中返回了结果,不需要再次计算。
4、权限控制
在特定的场景下,需要对函数进行权限控制,例如:只有管理员才能删除数据。下面是一个简单的权限控制装饰器实现:
` python
def admin_required(func):
def wrapper(*args, **kwargs):
if kwargs.get("user") != "admin":
raise Exception("Permission denied.")
else:
return func(*args, **kwargs)
return wrapper
@admin_required
def delete_data(user, data_id):
