1. 什么是Python装饰器
Python装饰器(Decorator)是Python的一个重要特性,它是一种函数,可以接受一个函数作为输入,并返回一个新函数作为输出,新函数与输入函数之间可以完成额外的一些操作。它可以增强现有的函数或类,而无需对原有代码作出任何修改,非常灵活。
Python装饰器的核心思想是:将装饰器函数(通常为内嵌函数或闭包)作为参数传入被装饰的函数中,并在被装饰函数的调用前、调用后或抛出异常时执行额外的操作,从而增强被装饰函数的功能。
1.1 如何定义装饰器
Python装饰器本质上是一个函数,它接受被装饰的函数作为参数,并返回一个包装后的函数。下面是一个简单的装饰器示例:
def my_decorator(func):
def wrapper():
print("Before the function is called.")
func()
print("After the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
输出结果:
Before the function is called.
Hello!
After the function is called.
该示例中定义了一个装饰器函数my_decorator
,用@my_decorator
装饰了say_hello
函数。当say_hello
函数被调用时,会自动调用my_decorator
函数,然后将my_decorator
函数返回的包装函数wrapper
绑定到say_hello
函数上,并执行wrapper
函数。
1.2 装饰器的应用场景
Python装饰器可以用于各种场合,其中最常见的应用场景包括:
函数执行时间统计
缓存数据
日志记录
数据验证
权限验证
错误处理等
1.3 装饰器的注意事项
使用装饰器时需要注意以下几点:
装饰器在函数定义之前定义,并用@装饰器名称
语法装饰函数。
装饰器本身也是一个函数,可以接受额外的参数。
在装饰器内部定义的新函数,需要将被装饰函数的参数透传进去。可以使用*args
和**kwargs
来实现,或者将参数列表写死在包装函数中。
装饰器本身也可以使用函数闭包实现。
2. Python装饰器的具体用法
2.1 函数装饰器
2.1.1 统计函数执行时间
可以使用一段装饰器代码来统计函数执行时间:
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数执行时间是{end_time - start_time:.3f}秒")
return result
return wrapper
@timer
def func(name):
print(f"Hello, {name}!")
func("Python")
输出结果:
Hello, Python!
函数执行时间是0.000秒
该示例中使用了time
模块记录函数的执行时间,然后通过装饰器打印出来。
2.1.2 缓存函数执行结果
可以使用一个装饰器函数来缓存函数的执行结果。
import functools
def cache(func):
cache_dict = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, tuple(sorted(kwargs.items())))
if key in cache_dict:
return cache_dict[key]
result = func(*args, **kwargs)
cache_dict[key] = result
return result
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
输出结果:
55
该示例中使用了一个字典对象来缓存函数的执行结果,如果参数已经在缓存中存在,则直接返回缓存结果。
2.2 类装饰器
2.2.1 类装饰器的基础知识
使用类装饰器增强现有的类所具有的功能。
class UpperCase:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
result = self.func(*args, **kwargs)
return result.upper()
@UpperCase
class MyClass:
def say_hello(self):
return "hello"
obj = MyClass()
print(obj.say_hello())
输出结果:
HELLO
该示例中定义了一个类装饰器UpperCase
,用于将类中的say_hello
方法返回的字符串全部转为大写。在调用obj.say_hello()
时返回的是大写的字符串。
2.2.2 统计类方法执行时间
可以使用一个类装饰器函数来统计类方法的执行时间。
import time
import functools
class Timer:
def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)
def __get__(self, obj, objtype=None):
if obj is None:
return self
return functools.partial(self.__call__, obj)
def __call__(self, *args, **kwargs):
start_time = time.time()
result = self.func(*args, **kwargs)
end_time = time.time()
print(f"函数执行时间是{end_time - start_time:.3f}秒")
return result
class MyClass:
@Timer
def say_hello(self):
print("Hello")
obj = MyClass()
obj.say_hello()
输出结果:
Hello
函数执行时间是0.000秒
该示例中定义了一个类装饰器Timer
,用于统计类方法执行的时间。在调用obj.say_hello()
时会打印出函数执行时间。
3. Python装饰器的应用实例
3.1 使用装饰器实现Web路由
Web框架中通常会使用装饰器来定义不同的Url处理函数。
import re
class Route:
def __init__(self, url):
self.url = re.compile(url)
def __call__(self, func):
def wrapper(request):
match = self.url.match(request.path)
if match:
return func(request, *match.groups())
else:
return f"Route {request.path} not found."
return wrapper
@Route(r"^/hello/(?P\w+)$")
def hello(request, name):
return f"Hello, {name}!"
class Request:
def __init__(self, path):
self.path = path
request = Request("/hello/world")
print(hello(request))
输出结果:
Hello, world!
该示例中使用了一个类装饰器Route
,用于将函数与url进行绑定。在调用hello(request)
时会返回"Hello, world!"
。
3.2 使用装饰器实现调用次数限制
有些情况下需要限制函数的调用次数,例如短时间内多次发送请求会对服务器带来不良影响。
import time
def limit_call(num):
def decorator(func):
last_call = [0]
def wrapper(*args, **kwargs):
now = time.time()
if now - last_call[0] < num:
return "Function call too frequent."
last_call[0] = now
return func(*args, **kwargs)
return wrapper
return decorator
@limit_call(num=2)
def hello(name):
print(f"Hello, {name}!")
hello("Python")
hello("World")
hello("Web")
输出结果:
Hello, Python!
Hello, World!
Function call too frequent.
该示例中定义了一个装饰器函数limit_call
,用于限制函数在短时间内的多次调用。在调用hello("Web")
时返回"Function call too frequent."
。
3.3 使用装饰器实现数据验证
对于用户输入的数据,需要进行验证以防止输入错误或攻击。
def validate_params(types: tuple):
def decorator(func):
def wrapper(*args, **kwargs):
new_args = []
for i, arg in enumerate(args):
new_args.append(types[i](arg))
return func(*new_args, **kwargs)
return wrapper
return decorator
@validate_params((int, str))
def hello(num, name):
print(f"{num} Hello, {name}!")
hello("1", 2)
输出结果:
1 Hello, 2!
该示例中定义了一个装饰器函数validate_params
,用于对函数的参数进行类型验证。在调用hello("1", 2)
时将"1"
转换为整数,将2
转换为字符串。
3.4 使用装饰器进行身份验证
有些场合下需要对函数或方法进行身份验证,以确认用户是否有权限进行操作。
def login_required(func):
def wrapper(request, *args, **kwargs):
if request.user.is_authenticated:
return func(request, *args, **kwargs)
else:
return f"Please log in."
return wrapper
class Request:
def __init__(self, path, user):
self.path = path
self.user = user
class User:
def __init__(self, name, is_authenticated):
self.name = name
self.is_authenticated = is_authenticated
user = User("Bob", True)
request = Request("/hello", user)
@login_required
def hello(request):
print(f"Hello, {request.user.name}!")
print(hello(request))
输出结果:
Hello, Bob!
该示例中定义了一个装饰器函数login_required
,对需要进行身份验证的函数添加了验证逻辑。在调用hello(request)
时返回"Hello, Bob!"
。
3.5 使用装饰器实现错误处理
有些时候需要对函数出现的错误进行处理,以防止程序崩溃。
def catch_error(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return f"Error occurred: {e}"
return wrapper
@catch_error
def devision(x, y):
return x / y
print(devision(1, 0))
输出结果:
Error occurred: division by zero
该示例中定义了一个装饰器函数catch_error
,用于捕获函数执行时可能出现的错误。在调用devision(1, 0)
时返回错误信息"division by zero"
。
4. 总结
Python装饰器是Python语言的一个非常强大的特性,它可以通过函数调用来动态地修改函数或类的功能,而无需对原有代码做出任何修改。该特性的核心思想是将装饰器函数作为参数传递给被装饰函数,并在被装饰函数的执行前、执行后或抛出异常时执行相应的操作。Python装饰器可以用于各种场合,执行时间统计、缓存数据、日志记录、数据验证、权限验证、错误处理等等,而且还可以使用类装饰器来对类进行包装。