首页
学习
活动
专区
圈层
工具
发布

Python 装饰器:掌握它,让你的代码优雅翻倍!

在 Python 开发中,常需在函数执行前后附加额外功能,如日志记录、性能监控等。若无装饰器,需在每个函数中重复编写相关代码,既冗余又破坏函数单一职责原则。Python 的装饰器提供了一种优雅的解决方案,它能在不修改原函数逻辑的前提下,为其增加新功能。装饰器的本质是“包装”函数或类,其常见形式是通过一个函数接收另一个函数,并返回一个新的函数。

装饰器基础

理解装饰器需先掌握两个关键概念:函数是一等公民和闭包。在 Python 中,函数可被赋值给变量、作为参数传递,也可作为返回值返回,这为装饰器的实现提供了基础。闭包则是内部函数引用外部函数变量的现象,即使外部函数已结束运行,内部函数仍可使用这些变量。

基于此,装饰器的核心是定义一个函数,接收另一个函数作为参数并返回新函数,从而在不修改原函数代码的情况下,为其添加额外功能。例如,以下是一个简单的装饰器示例:

def simple_decorator(func):

def wrapper():

print("Before the function runs")

func()

print("After the function runs")

return wrapper

@simple_decorator

def say_hello():

print("Hello!")

say_hello()

输出结果为:

Before the function runs

Hello!

After the function runs

装饰器的进阶用法

保留函数元信息:functools.wraps

使用装饰器时,原函数的元信息(如 __name__、__doc__ 等)会被替换为包装函数的信息。为保留原函数的元信息,需使用 functools.wraps。例如:

from functools import wraps

def simple_decorator(func):

@wraps(func)

def wrapper(*args, **kwargs):

return func(*args, **kwargs)

return wrapper

@simple_decorator

def greet(name):

"""Say hello to someone"""

return f"Hello, {name}!"

print(greet.__name__) # greet

print(greet.__doc__) # Say hello to someone

类装饰器

除了函数,也可用类实现装饰器。关键在于定义 __call__ 方法,使类实例能像函数一样被调用。例如:

class Repeat:

def __init__(self, times):

self.times = times

def __call__(self, func):

def wrapper(*args, **kwargs):

result = None

for _ in range(self.times):

result = func(*args, **kwargs)

return result

return wrapper

@Repeat(3)

def say_hi(name):

print(f"Hi, {name}!")

say_hi("Alice")

装饰器叠加

Python 允许在一个函数上叠加多个装饰器,执行顺序是自下而上。例如:

def decorator_a(func):

def wrapper(*args, **kwargs):

print("Decorator A before")

result = func(*args, **kwargs)

print("Decorator A after")

return result

return wrapper

def decorator_b(func):

def wrapper(*args, **kwargs):

print("Decorator B before")

result = func(*args, **kwargs)

print("Decorator B after")

return result

return wrapper

@decorator_a

@decorator_b

def say_hello():

print("Hello!")

say_hello()

输出结果为:

Decorator A before

Decorator B before

Hello!

Decorator B after

Decorator A after

带参数的装饰器工厂

若希望装饰器本身也能接收参数,需再多写一层函数,即装饰器工厂。例如:

from functools import wraps

def repeat(times):

def decorator(func):

@wraps(func)

def wrapper(*args, **kwargs):

for _ in range(times):

func(*args, **kwargs)

return wrapper

return decorator

@repeat(3)

def greet(name):

print(f"Hello, {name}!")

greet("Alice")

实用库中的装饰器

装饰器在 Python 标准库和第三方框架中应用广泛。例如,标准库中的 @staticmethod、@classmethod、@property 和 @functools.lru_cache 等装饰器,以及第三方框架 Flask 的路由装饰器、Django 的权限装饰器和 Click 的命令行装饰器等,都极大地简化了代码,提高了开发效率。

最佳实践与注意事项

避免滥用:装饰器适用于处理横切逻辑,如日志、缓存、权限验证等,但不宜过度使用,以免影响代码可读性。

始终使用 functools.wraps:为保留原函数的元信息,自定义装饰器应加上 @wraps。

注意副作用:装饰器可能改变函数的调用方式或引入缓存、权限等逻辑,需清楚其可能带来的副作用,并在必要时提供关闭或绕过的机制。

保持可读性:复杂的装饰器逻辑应写清楚,或在文档和注释中解释清楚其作用和顺序,必要时可用类装饰器或明确的函数调用替代。

关注性能:装饰器会增加函数调用层级,复杂装饰器或过多装饰器可能影响性能,需在必要时通过性能分析工具确认是否存在瓶颈。

总结

本文介绍了 Python 装饰器的基础语法、进阶用法及在标准库和第三方框架中的应用。装饰器能有效剥离横切逻辑,减少重复代码,提高代码可读性和扩展性。使用时需遵循最佳实践,避免滥用,关注可读性、性能和副作用,从而在合适的场景中自然地使用装饰器,提升代码质量。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OB1P-yc0wcu_OOd2s0HMRBlQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券