You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 09:17:04