Python 装饰器与装饰器设计模式:主要区别
比较 Python 装饰器和经典的装饰器设计模式。了解定义时函数包装和使用可运行代码的运行时动态对象组合之间的差异。
虽然 Python 装饰器和装饰器设计模式具有相同的名称和“包装”代码以扩展行为的一般概念,但它们代表了完全不同的编程结构和哲学。
Python 装饰器是一种语言功能 - 语法糖,允许您在定义时包装函数、方法或类。它是一种元编程形式,在导入或定义模块时执行,改变该函数在应用程序运行的其余部分的行为方式。
装饰器设计模式最初由四人帮(GoF)定义,是一种面向对象的结构设计模式。它旨在通过使用包装类和动态组合,在运行时动态地向各个对象添加职责,而不影响同一类的其他对象。
特性比较矩阵
| Feature | Python | Other Language |
|---|---|---|
| 核心理念 | 用于包装函数/类的语言级语法糖。 | 用于向单个对象动态添加行为的设计模式。 |
| 执行时间 | 定义时间(加载模块时运行一次)。 | 运行时(在程序执行期间动态实例化)。 |
| 目标实体 | 函数、方法或整个类(静态范围)。 | 特定对象实例(动态范围)。 |
| 实施方法 | 使用“@decorator”语法的闭包和辅助函数。 | 类继承、接口对齐和对象组合。 |
Syntax & Idiom Comparisons
01.Python 装饰器:函数包装器(定义时间)
Python 装饰器利用一流函数来拦截调用。当您编写“@my_decorator”时,Python 将装饰函数传递给您的装饰函数,并将返回的包装函数绑定到原始函数名称。
在此示例中,“@log_call”装饰器包装了一个简单的函数来跟踪它的执行时间和返回内容。请注意,这种包装在定义函数时就已确定。
def log_call(func):
def wrapper(*args, **kwargs):
print(f"[LOG] Executing '{func.__name__}' with args {args}")
result = func(*args, **kwargs)
print(f"[LOG] '{func.__name__}' returned: {result}")
return result
return wrapper
@log_call
def add(a, b):
return a + b
# The wrapping occurs once at definition time
print("Result of add(5, 7):", add(5, 7))// Decorators in Python are syntactic sugar for function wrappers:
// add = log_call(add)
// They exist primarily to avoid duplicate boilerplate for logging, timing, and auth.02.装饰器设计模式:对象包装(运行时组合)
经典的装饰设计模式使用类组合。您有一个基本组件类和包装此基本组件的装饰器类。在运行时,您将这些构造函数链接在一起以动态构建自定义对象。
在这里,我们实现了一个经典的咖啡装饰器系统。我们可以将基本的 Coffee 对象包装在 MilkDecorator 中,并将其包装在 SugarDecorator 中,在运行时动态计算最终成本。
class Coffee:
def get_cost(self):
return 5.0
def get_description(self):
return "Simple Coffee"
class MilkDecorator:
def __init__(self, coffee):
self.coffee = coffee
def get_cost(self):
return self.coffee.get_cost() + 1.5
def get_description(self):
return self.coffee.get_description() + ", Milk"
class SugarDecorator:
def __init__(self, coffee):
self.coffee = coffee
def get_cost(self):
return self.coffee.get_cost() + 0.5
def get_description(self):
return self.coffee.get_description() + ", Sugar"
# Chaining wrappers dynamically at runtime
my_drink = Coffee()
my_drink = MilkDecorator(my_drink)
my_drink = SugarDecorator(my_drink)
print("Drink description:", my_drink.get_description())
print("Total Cost: $", my_drink.get_cost())// Standard OO languages (like Java) require shared interfaces:
// interface Coffee { double getCost(); }
// class SimpleCoffee implements Coffee { ... }
// class MilkDecorator implements Coffee { ... }
// Python's duck typing allows dynamic composition without interfaces.判决与总结
当您想要将横切关注点(例如日志记录、计时、身份验证、缓存或输入验证)应用到整个应用程序中的函数或路由时,请使用 Python 装饰器 (@decorator)。
当您需要在运行时动态地附加或分离单个对象实例的行为时,尤其是在以任意序列组合多个可选包装类时,请使用装饰器设计模式。
常见问题解答
为什么维基百科页面说 Python 装饰器不是装饰器模式?
因为标准 Python 装饰器在编译/定义时包装函数和类定义,从而影响该函数的所有实例。经典设计模式在运行时动态包装各个对象实例。
GoF 装饰器模式在 Python 中有用吗?
是的,但是 Python 的鸭子类型使其实现比 Java 或 C++ 等静态类型语言简单得多,因为装饰器不需要从正式的基接口继承。
其他比较
推荐的 Python 资源
通过相关的交互式教程、备忘单和代码比较来扩展您的知识。