您现在的位置是:首页 >技术杂谈 >用ChatGPT来分析Python装饰器有那些高级使用场景并给出案例网站首页技术杂谈
用ChatGPT来分析Python装饰器有那些高级使用场景并给出案例
问题一:python 装饰器都有那些高级使用场景
Python装饰器是一种高级的语言特性,它可以用于修改、扩展或包装函数和类的行为,使代码更加优雅和简洁。以下是一些常见的高级使用场景:
1. 权限控制:可以使用装饰器来限制某些操作只能由具有特定权限的用户执行。
class User:
def __init__(self, name, role):
self.name = name
self.role = role
class UserRole:
ADMIN = "admin"
USER = "user"
def role_required(roles):
def decorator(func):
def wrapper(user, *args, **kwargs):
if user.role not in roles:
raise ValueError(f"User '{user.name}' doesn't have the required role to access this resource")
return func(user, *args, **kwargs)
return wrapper
return decorator
@role_required([UserRole.ADMIN])
def delete_user(user, user_id):
print(f"User '{user.name}' with role '{user.role}' is deleting user with ID '{user_id}'")
@role_required([UserRole.ADMIN, UserRole.USER])
def view_user_profile(user, user_id):
print(f"User '{user.name}' with role '{user.role}' is viewing profile of user with ID '{user_id}'")
if __name__ == "__main__":
admin = User("admin", UserRole.ADMIN)
user1 = User("user1", UserRole.USER)
user2 = User("user2", UserRole.USER)
delete_user(admin, 123) # Output: User 'admin' with role 'admin' is deleting user with ID '123'
delete_user(user1, 123) # Output: ValueError: User 'user1' doesn't have the required role to access this resource
view_user_profile(admin, 123) # Output: User 'admin' with role 'admin' is viewing profile of user with ID '123'
view_user_profile(user1, 123) # Output: User 'user1' with role 'user' is viewing profile of user with ID '123'
view_user_profile(user2, 123) # Output: User 'user2' with role 'user' is viewing profile of user with ID '123'
view_user_profile(User("guest", ""), 123) # Output: ValueError: User 'guest' doesn't have the required role to access this resource
在上述代码中,我们定义了一个名为User
的类,表示系统中的用户。每个用户都有一个name
属性和一个role
属性,分别表示用户的名称和角色。
然后,我们定义了一个名为UserRole
的类,其中定义了两个静态变量ADMIN
和USER
,分别表示系统中的管理员和普通用户角色。
接着,我们定义了一个名为role_required()
的装饰器,它接受一个角色列表作为参数,并返回一个新的装饰器。新的装饰器接受一个函数作为参数,并返回一个新的函数。新的函数首先检查传入的用户是否具有所需的角色,如果不具备则抛出ValueError
异常。然后调用原函数,并传入原来的参数。最后返回原函数的返回值。
接下来,我们定义了两个函数delete_user()
和view_user_profile()
,并在它们上方应用了@role_required
装饰器。delete_user()
函数要求用户具有管理员角色,而view_user_profile()
函数要求用户具有管理员或普通用户角色。
在__main__
函数中,我们创建了一个管理员用户和两个普通用户。然后我们分别调用delete_user()
和view_user_profile()
函数,传入不同的用户和资源ID参数。输出结果表明,只有管理员用户才有权限删除用户,而管理员和普通用户都有权限查看用户资料。
如果尝试使用不具备所需角色的用户来访问资源,则会引发ValueError
异常。
这个例子展示了如何使用装饰器实现基于角色的访问控制。这种方式可以在许多不同的应用程序中使用,例如 Web 应用程序、API、命令行工具等等。通过使用装饰器,我们可以轻松地将访问控制逻辑与应用程序的其他部分分离开来,使代码更易于维护和扩展。
2. 缓存优化:可以使用装饰器来实现函数的结果缓存,从而避免重复计算。
import time
def cache(func):
results = {}
def wrapper(*args):
if args in results:
return results[args]
else:
result = func(*args)
results[args] = result
return result
return wrapper
@cache
def expensive_calculation(x, y):
time.sleep(1)
return x * y
if __name__ == "__main__":
start_time = time.time()
print(expensive_calculation(2, 3)) # Output: 6 (takes 1 second to run)
end_time = time.time()
print(f"Time taken: {end_time - start_time:.2f} seconds")
start_time = time.time()
print(expensive_calculation(2, 3)) # Output: 6 (returns immediately from cache)
end_time = time.time()
print(f"Time taken: {end_time - start_time:.2f} seconds")
start_time = time.time()
print(expensive_calculation(3, 4)) # Output: 12 (takes 1 second to run)
end_time = time.time()
print(f"Time taken: {end_time - start_time:.2f} seconds")
start_time = time.time()
print(expensive_calculation(3, 4)) # Output: 12 (returns immediately from cache)
end_time = time.time()
print(f"Time taken: {end_time - start_time:.2f} seconds")
在上述代码中,我们定义了一个名为cache()
的装饰器。它接受一个函数作为参数,并返回一个新的函数。新的函数用一个名为results
的字典来保存每个参数的计算结果。如果在results
中找到了相同的参数,则直接从缓存中返回结果。否则,调用原函数进行计算,并将结果存储在缓存中,然后返回结果。
接下来,我们定义了一个名为expensive_calculation()
的函数,并在其上方应用了@cache
装饰器。expensive_calculation()
函数模拟了一个耗时的计算,并返回两个参数的乘积。
在__main__
函数中,我们多次调用expensive_calculation()
函数,传入不同的参数。第一次调用需要计算,然后将结果存储在缓存中。后续的调用将直接从缓存中返回结果,而无需重新计算。
这个例子展示了如何使用装饰器实现缓存优化。这种方式可以在许多需要频繁计算的场景中使用,例如数据库查询、API 请求等等。通过使用装饰器,我们可以轻松地将缓存逻辑与应用程序的其他部分分离开来,使代码更易于维护和扩展。
3. 日志记录:可以使用装饰器来记录函数的输入、输出和执行时间,从而方便调试和性能优化。
import logging
def log(func):
def wrapper(*args, **kwargs):
logging.basicConfig(filename='example.log', level=logging.INFO)
logging.info(f"Running {func.__name__} with args={args} kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@log
def add(x, y):
return x + y
if __name__ == "__main__":
print(add(2, 3)) # Output: 5
在上述代码中,我们定义了一个名为log()
的装饰器。它接受一个函数作为参数,并返回一个新的函数。新的函数在调用原函数之前,首先使用logging
模块记录一个日志。日志文件的名称为example.log
,日志级别为INFO
,日志信息包含原函数的名称和参数列表。然后,新的函数调用原函数并返回结果。
接下来,我们定义了一个名为add()
的函数,并在其上方应用了@log
装饰器。add()
函数简单地将两个参数相加,并返回结果。
在__main__
函数中,我们调用add()
函数,并传入两个参数。装饰器记录了一个日志,并返回了计算结果。
这个例子展示了如何使用装饰器实现日志记录。这种方式可以在许多需要记录操作日志的场景中使用,例如 Web 应用程序、API、命令行工具等等。通过使用装饰器,我们可以轻松地将日志记录逻辑与应用程序的其他部分分离开来,使代码更易于维护和扩展。
4. 事务处理:可以使用装饰器来实现数据库事务处理,从而保证操作的原子性和一致性。
import sqlite3
def transaction(func):
def wrapper(*args, **kwargs):
conn = sqlite3.connect("mydb.db")
try:
result = func(conn, *args, **kwargs)
conn.commit()
return result
except:
conn.rollback()
raise
finally:
conn.close()
return wrapper
@transaction
def transfer_money(conn, from_account, to_account, amount):
cur = conn.cursor()
cur.execute("SELECT balance FROM accounts WHERE id = ?", (from_account,))
from_balance = cur.fetchone()[0]
if from_balance < amount:
raise ValueError("Insufficient funds")
cur.execute("SELECT balance FROM accounts WHERE id = ?", (to_account,))
to_balance = cur.fetchone()[0]
cur.execute("UPDATE accounts SET balance = ? WHERE id = ?", (from_balance - amount, from_account))
cur.execute("UPDATE accounts SET balance = ? WHERE id = ?", (to_balance + amount, to_account))
return "Transaction successful"
if __name__ == "__main__":
print(transfer_money(123456, 789012, 100.0))
在上述代码中,我们定义了一个名为transaction()
的装饰器。它接受一个函数作为参数,并返回一个新的函数。新函数使用一个sqlite3
数据库连接对象作为第一个参数,以及其他任意位置和关键字参数。它在事务中执行原始函数,如果原始函数成功完成,则提交事务;否则回滚事务并抛出异常。最后,它关闭数据库连接。
接下来,我们定义了一个名为transfer_money()
的函数,并在其上方应用了@transaction
装饰器。transfer_money()
函数模拟了从一个账户向另一个账户转移资金的过程。在数据库中,我们假设存在一个名为accounts
的表,其中每一行表示一个账户,包括一个账户ID和当前余额。在transfer_money()
函数中,我们首先查询源账户的余额,如果余额不足,则抛出一个ValueError
异常。然后,我们查询目标账户的余额,并执行两个UPDATE
语句来更新源账户和目标账户的余额。如果一切正常,我们返回一个表示事务成功的消息。
在__main__
函数中,我们调用transfer_money()
函数一次,并传递了两个账户ID和转移金额。如果一切正常,该函数应该返回一个表示事务成功的消息。否则,它会抛出一个异常并回滚事务。这个例子展示了如何使用装饰器实现事务处理。这种方式可以在许多需要使用事务的场景中使用,例如 Web 应用程序、API、数据处理等等。通过使用装饰器,我们可以轻松地将事务处理逻辑与应用程序的其他部分分离开来,使代码更易于维护和扩展。
5. 面向切面编程(AOP):可以使用装饰器来实现AOP,从而实现代码的横向切割。
import time
def timing_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} took {end_time - start_time:.2f} seconds to complete")
return result
return wrapper
@timing_decorator
def slow_function():
time.sleep(2)
print("Slow function completed")
if __name__ == "__main__":
slow_function()
在上面的代码中,我们定义了一个名为 timing_decorator
的装饰器函数。这个装饰器可以用于任何函数,它会测量被装饰函数的执行时间,并在函数执行完后输出执行时间。装饰器函数接收被装饰函数作为参数,并返回一个新的函数。这个新的函数使用 *args
和 **kwargs
来接收任意数量和类型的位置和关键字参数。
接下来,我们使用 @timing_decorator
装饰器来修饰一个名为 slow_function
的函数。这个函数会睡眠 2 秒钟,并在睡眠结束后输出一条消息。当我们调用 slow_function
时,实际上是调用被装饰后的函数 wrapper
,它会在函数执行前和执行后记录时间,并输出执行时间。
这个例子展示了如何使用装饰器实现面向切面编程(AOP)。我们可以将一些通用的、与具体业务无关的逻辑,如日志记录、错误处理、性能监控等,提取到装饰器中。这样,我们就可以将这些逻辑与业务逻辑分离,避免代码冗余和混乱。通过装饰器,我们可以轻松地将这些通用逻辑应用到多个函数中,实现代码的复用和可维护性。
6. 类型检查:可以使用装饰器来实现函数参数类型检查和返回值类型检查,从而提高代码的健壮性。
def type_check_decorator(func):
def wrapper(*args, **kwargs):
for arg, expected_type in zip(args, func.__annotations__.values()):
if not isinstance(arg, expected_type):
raise TypeError(f"Expected {expected_type} but got {type(arg)}")
for name, arg in kwargs.items():
expected_type = func.__annotations__.get(name)
if expected_type and not isinstance(arg, expected_type):
raise TypeError(f"Expected {expected_type} but got {type(arg)}")
result = func(*args, **kwargs)
expected_return_type = func.__annotations__.get("return")
if expected_return_type and not isinstance(result, expected_return_type):
raise TypeError(f"Expected {expected_return_type} but got {type(result)}")
return result
return wrapper
@type_check_decorator
def add(x: int, y: int) -> int:
return x + y
if __name__ == "__main__":
print(add(1, 2)) # Output: 3
print(add("1", 2)) # Output: TypeError: Expected <class 'int'> but got <class 'str'>
print(add(1, "2")) # Output: TypeError: Expected <class 'int'> but got <class 'str'>
print(add(1, 2.0)) # Output: TypeError: Expected <class 'int'> but got <class 'float'>
在上述代码中,我们定义了一个名为type_check_decorator
的装饰器,它接受一个函数作为参数,并返回一个新的函数。新的函数首先检查函数的参数和返回值类型是否符合函数定义中的类型注解,如果不符合则抛出TypeError
异常。如果检查通过,则调用原函数并返回结果。
接着,我们定义了一个add()
函数,并在其上方应用了@type_check_decorator
装饰器。add()
函数的参数和返回值都带有类型注解,它们分别表示参数x
和y
都是int
类型,返回值是int
类型。
当我们分别调用add(1, 2)
和add("1", 2)
时,它们分别输出了3
和TypeError: Expected <class 'int'> but got <class 'str'>
。这说明我们成功地使用装饰器实现了函数参数类型检查和返回值类型检查的效果。
7. 参数验证:可以使用装饰器来验证函数参数的合法性,从而避免潜在的安全问题。
def check_args(func):
def wrapper(*args, **kwargs):
for arg in args:
if not isinstance(arg, (int, float)):
raise TypeError(f"Invalid argument type: {type(arg)}")
for key, value in kwargs.items():
if not isinstance(value, (int, float)):
raise TypeError(f"Invalid argument type: {type(value)}")
return func(*args, **kwargs)
return wrapper
@check_args
def add(x, y):
return x + y
if __name__ == "__main__":
print(add(2, 3)) # Output: 5
print(add(2, "3")) # Raises: TypeError: Invalid argument type: <class 'str'>
在上述代码中,我们定义了一个名为check_args()
的装饰器。它接受一个函数作为参数,并返回一个新的函数。新函数使用*args
和**kwargs
来接受所有传递给原始函数的位置和关键字参数。然后,它循环遍历所有参数,并检查它们的类型是否为int
或float
。如果任何参数的类型不是int
或float
,则抛出一个TypeError
异常。如果所有参数的类型都是有效的,则调用原始函数并返回结果。
接下来,我们定义了一个名为add()
的函数,并在其上方应用了@check_args
装饰器。add()
函数简单地将两个参数相加,并返回结果。
在__main__
函数中,我们调用add()
函数两次。第一次传递了两个整数作为参数,它返回了正确的结果。第二次传递了一个整数和一个字符串作为参数,它抛出了一个TypeError
异常,因为字符串不是一个有效的参数类型。
这个例子展示了如何使用装饰器实现参数检查。这种方式可以在许多需要检查参数类型的场景中使用,例如 Web 应用程序、API、命令行工具等等。通过使用装饰器,我们可以轻松地将参数检查逻辑与应用程序的其他部分分离开来,使代码更易于维护和扩展。
8. 运行时检查:可以使用装饰器来在运行时对函数进行检查,从而发现潜在的错误或问题。
def type_check_decorator(func):
def wrapper(*args, **kwargs):
for arg, expected_type in zip(args, func.__annotations__.values()):
if not isinstance(arg, expected_type):
raise TypeError(f"Expected {expected_type} but got {type(arg)}")
for name, arg in kwargs.items():
expected_type = func.__annotations__.get(name)
if expected_type and not isinstance(arg, expected_type):
raise TypeError(f"Expected {expected_type} but got {type(arg)}")
return func(*args, **kwargs)
return wrapper
@type_check_decorator
def add(x: int, y: int) -> int:
return x + y
if __name__ == "__main__":
print(add(1, 2)) # Output: 3
print(add("1", 2)) # Output: TypeError: Expected <class 'int'> but got <class 'str'>
在上述代码中,我们定义了一个名为type_check_decorator
的装饰器,它接受一个函数作为参数,并返回一个新的函数。新的函数首先检查函数的参数和返回值类型是否符合函数定义中的类型注解,如果不符合则抛出TypeError
异常。如果检查通过,则调用原函数并返回结果。
接着,我们定义了一个add()
函数,并在其上方应用了@type_check_decorator
装饰器。add()
函数的参数和返回值都带有类型注解,它们分别表示参数x
和y
都是int
类型,返回值是int
类型。
当我们分别调用add(1, 2)
和add("1", 2)
时,它们分别输出了3
和TypeError: Expected <class 'int'> but got <class 'str'>
。这说明我们成功地使用装饰器实现了函数参数类型检查和返回值类型检查的效果。
9. 多线程处理:可以使用装饰器来实现多线程或异步处理,从而提高代码的性能和效率。
import asyncio
def async_decorator(func):
async def wrapper(*args, **kwargs):
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, func, *args, **kwargs)
return wrapper
@async_decorator
def download(url):
# 模拟下载操作
print(f"Start downloading {url}")
asyncio.sleep(2)
print(f"Finish downloading {url}")
async def main():
tasks = [download("http://example.com"), download("http://example.org")]
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())
在上述代码中,我们定义了一个名为async_decorator
的装饰器,它接受一个异步函数作为参数,并返回一个新的异步函数。新的异步函数使用asyncio.run_in_executor()
方法来在一个线程池中执行原函数,从而实现了异步处理的效果。
接着,我们定义了一个download()
函数,并在其上方应用了@async_decorator
装饰器。在main()
函数中,我们创建了两个下载任务,并使用asyncio.gather()
方法来同时执行它们。
当我们运行这个程序时,它会输出以下内容:
Start downloading http://example.com
Start downloading http://example.org
Finish downloading http://example.com
Finish downloading http://example.org
可以看到,两个下载任务是同时进行的,并在2秒后完成了。这说明我们成功地使用装饰器实现了异步处理的效果。
总之,装饰器是一种非常强大和灵活的语言特性,可以用于实现各种高级功能和编程范式。掌握装饰器的使用方法,对于Python程序员来说是非常重要的。