如何用Python装饰器实现函数多态?场景示例与实现困惑
如何用装饰器实现Python中的函数多态?
问题场景
我们希望给PersonalChef类的cook方法实现多态行为——根据传入的时段参数,执行对应的烹饪逻辑。
初始的非装饰器实现方式如下:
class PersonalChef(): def cook(self, time_of_day): meal = {'morning':'breakfast', 'midday':'lunch', 'evening':'dinner'}[time_of_day] print("Cooking", meal) PersonalChef().cook('morning') # 输出: Cooking breakfast
但我们期望用更优雅的装饰器语法实现,让不同时段的cook逻辑分开定义,同时支持子类轻松重写特定时段的行为,理想效果如下:
基类实现
class PersonalChef(): @at_time('morning') def cook(self): print("Cooking breakfast") @at_time('midday') def cook(self): print("Cooking lunch") @at_time('evening') def cook(self): print("Cooking dinner") PersonalChef().cook('morning') # 输出: Cooking breakfast
子类扩展
子类只需重写特定时段的逻辑,无需了解基类内部实现:
class PersonalChefUK(PersonalChef): @at_time('evening') def cook(self): print("Cooking supper")
这种方式能将复杂度封装在底层,降低派生类出错的风险,但直接实现时会遇到同名方法覆盖、调度逻辑缺失等问题,下面是具体的解决方案。
实现方案
要实现这个需求,需要两个核心部分:标记用的装饰器和收集并调度方法的元类(元类负责在类创建时处理装饰过的方法)。
1. 实现at_time装饰器
这个装饰器的作用是给每个cook方法打上对应时段的标记,方便后续收集:
def at_time(time_of_day): def decorator(func): # 给函数添加属性,记录对应的时段 func._time_of_day = time_of_day return func return decorator
2. 实现元类ChefMeta
元类会在类定义完成时,收集所有带标记的cook方法,生成一个调度函数替换原有的cook:
class ChefMeta(type): def __new__(cls, name, bases, attrs): # 初始化处理函数注册表,先继承基类的逻辑 cook_handlers = {} for base in bases: if hasattr(base, '_cook_handlers'): cook_handlers.update(base._cook_handlers) # 收集当前类中所有带_time_of_day标记的cook方法 for attr_name, func in attrs.items(): if attr_name == 'cook' and hasattr(func, '_time_of_day'): cook_handlers[func._time_of_day] = func # 保存注册表到类属性 attrs['_cook_handlers'] = cook_handlers # 定义调度用的cook方法:根据时段调用对应逻辑 def cook(self, time_of_day): handler = self._cook_handlers.get(time_of_day) if not handler: raise ValueError(f"未找到对应时段的烹饪逻辑:{time_of_day}") # 调用绑定了实例的处理函数 return handler(self) # 替换原cook方法为调度函数 attrs['cook'] = cook return super().__new__(cls, name, bases, attrs)
3. 定义基类和子类
让PersonalChef使用上述元类,子类自然继承该逻辑:
class PersonalChef(metaclass=ChefMeta): @at_time('morning') def cook(self): print("Cooking breakfast") @at_time('midday') def cook(self): print("Cooking lunch") @at_time('evening') def cook(self): print("Cooking dinner") class PersonalChefUK(PersonalChef): @at_time('evening') def cook(self): print("Cooking supper")
4. 测试验证
# 测试基类 chef = PersonalChef() chef.cook('morning') # 输出: Cooking breakfast chef.cook('evening') # 输出: Cooking dinner # 测试子类(重写了evening时段) uk_chef = PersonalChefUK() uk_chef.cook('evening') # 输出: Cooking supper uk_chef.cook('midday') # 输出: Cooking lunch
关键要点说明
- 避免同名方法覆盖:元类会收集所有装饰过的
cook方法,而不是让后定义的方法直接覆盖之前的,解决了类中同名方法的冲突问题。 - 实例方法绑定:调度函数会将
self传递给对应的处理函数,确保实例方法能正确访问类的属性和其他方法。 - 继承支持:元类会先合并基类的处理函数注册表,再添加当前类的逻辑,子类可以轻松覆盖特定时段的实现,同时继承其他时段的行为。
内容的提问来源于stack exchange,提问作者Jurgen Hissen
相关产品推荐
相关产品推荐

