python装饰器介绍
Python装饰器是一种用来修改或增加函数或类的功能的特殊函数。它们可以让你在不改变原始代码的情况下,给函数或类添加一些额外的操作,比如打印日志、检查权限、缓存结果等。装饰器的用法是在被装饰的函数或类的定义前面加上一个@符号,后面跟着装饰器函数的名称,例如:
@log
def hello(name):
print(f"Hello, {name}!")
这相当于执行了:
hello = log(hello)
这样,当你调用hello函数时,实际上是调用了log函数返回的新函数,这个新函数会在执行原始的hello函数之前或之后做一些额外的操作。你可以使用多个装饰器来装饰同一个函数或类,只需要按照顺序写在定义前面即可,例如:
@log
@timer
def hello(name):
print(f"Hello, {name}!")
这相当于执行了:
hello = log(timer(hello))
这样,当你调用hello函数时,实际上是调用了log函数返回的新函数,这个新函数会在执行timer函数返回的新函数之前或之后做一些额外的操作,而timer函数返回的新函数会在执行原始的hello函数之前或之后做一些额外的操作。你可以看到,装饰器的作用是把原始的函数或类包裹在一层又一层的新函数或类中,从而形成一个函数或类的堆栈,每一层都可以添加一些新的功能。
python装饰器的实现
那么,Python装饰器是如何实现的呢?其实,Python装饰器的本质就是一个高阶函数,也就是一个接受函数或类作为参数,并返回一个新的函数或类的函数。装饰器函数通常会定义一个内部函数或类,用来包裹被装饰的函数或类,并在内部函数或类中执行一些额外的操作,然后返回这个内部函数或类。例如,一个简单的log装饰器可以这样定义:
def log(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with {args} and {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
这个装饰器函数接受一个函数作为参数,并定义了一个内部函数wrapper,这个内部函数会在调用原始函数之前和之后打印一些日志信息,并返回原始函数的结果。然后,装饰器函数返回这个内部函数。这样,当我们用@log装饰一个函数时,实际上就是把这个函数替换成了wrapper函数,从而实现了日志功能。
python装饰器的用处
Python装饰器有什么用处呢?Python装饰器可以让你在不修改原始代码的情况下,给函数或类添加一些通用的功能,这样可以提高代码的复用性和可维护性。例如,你可以用装饰器来实现以下的功能:
- 缓存:你可以用装饰器来缓存函数的返回值,这样可以避免重复计算,提高性能。例如,你可以用@lru_cache装饰器来实现一个最近最少使用(LRU)的缓存策略,这个装饰器会自动缓存函数的最近调用结果,并在缓存满了之后,删除最久未使用的结果。这个装饰器可以用来加速一些耗时的计算,比如斐波那契数列的计算:
from functools import lru_cache
@lru_cache(maxsize=128)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
- 权限检查:你可以用装饰器来检查函数或类的调用者是否具有相应的权限,这样可以保证安全性。例如,你可以用@login_required装饰器来检查用户是否已经登录,如果没有登录,就重定向到登录页面。这个装饰器可以用来保护一些需要登录才能访问的网页,比如个人信息页面:
from flask import session, redirect, url_for
def login_required(func):
def wrapper(*args, **kwargs):
if 'user' not in session:
return redirect(url_for('login'))
return func(*args, **kwargs)
return wrapper
@login_required
def profile():
return f"Welcome, {session['user']}!"
- 性能测试:你可以用装饰器来测试函数或类的运行时间,这样可以优化性能。例如,你可以用@timeit装饰器来打印函数的运行时间,这个装饰器会在函数开始和结束时记录时间,并计算时间差。这个装饰器可以用来分析一些耗时的操作,比如数据库查询:
import time
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start} seconds")
return result
return wrapper
@timeit
def query(sql):
# do some database operations
return result
- 日志管理:我们可以用装饰器来给函数或类添加日志管理的功能,从而记录函数或类的调用信息、参数、返回值、执行时间等,方便我们进行调试和分析。例如,我们可以用@logged装饰器来打印函数的调用信息和返回值:
@logged(level="info")
def add(x, y):
return x + y
python装饰器的进阶用法
Python装饰器的进阶用法主要包括以下几个方面:
- 带参数的装饰器:有时我们需要给装饰器传递一些参数,来控制装饰器的行为。例如,我们可以给装饰器传递一个日志级别的参数,来决定装饰器打印的日志信息的详细程度。要实现带参数的装饰器,我们需要定义一个返回装饰器的函数,然后在使用装饰器时,先调用这个函数,再调用返回的装饰器。例如:
def logged(level):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if level == "info":
print(f"Calling {func.__name__} with {args} and {kwargs}")
elif level == "debug":
start = time.time()
print(f"Calling {func.__name__} with {args} and {kwargs}")
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start} seconds")
return result
return wrapper
return decorator
@logged(level="debug")
def add(x, y):
return x + y
- 装饰类:有时我们需要给类添加一些功能,而不是给函数添加。这时我们可以用装饰器来装饰类,从而修改或增加类的属性或方法。要实现装饰类,我们需要定义一个接受类作为参数,并返回一个新的类的函数,然后在使用装饰器时,把它放在类的定义前面。例如,我们可以用装饰器来给类添加一个类属性,记录类的创建时间:
def timestamped(cls):
@wraps(cls)
def new_init(self, *args, **kwargs):
self._created = time.time()
cls.__init__(self, *args, **kwargs)
cls.__init__ = new_init
return cls
@timestamped
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
- 装饰器堆叠:有时我们需要给一个函数或类应用多个装饰器,从而实现多个功能的叠加。这时我们可以用装饰器堆叠的方式,把多个装饰器按照顺序写在函数或类的定义前面,每个装饰器用一个@符号表示。这样,每个装饰器都会对上一个装饰器返回的结果进行装饰,最终得到一个多层装饰的结果。例如,我们可以用装饰器堆叠的方式,给一个函数添加日志、缓存和性能测试的功能:
@logged(level="info")
@lru_cache(maxsize=128)
@timeit
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)