在Python中,如何/应该使用装饰器来实现函数多态?

问题描述 投票:0回答:1

假设我们有一堂课如下:

class PersonalChef():
    def cook():
        print("cooking something...")

我们希望它烹饪的东西是一天中不同时间的函数,我们可以这样做:

class PersonalChef():
    def cook(time_of_day):
        ## a few ways to do this, but this is quite concise:
        meal = {'morning':'breakfast', 'midday':'lunch', 'evening':'dinner'}[time_of_day]
        print("Cooking", meal)

PersonalChef().cook('morning')
>>> Cooking breakfast

一个可能不错的语法形式是使用装饰器。有了一些隐藏在内部的机器

at_time
,应该可以让它像这样工作:

class PersonalChef():
    @at_time('morning')
    def cook():
        print("Cooking breakfast")

    @at_time('midday')
    def cook():
        print("Cooking lunch")

    @at_time('evening')
    def cook():
        print("Cooking dinner")

PersonalChef().cook('morning')
>>> Cooking breakfast

这可能是一种很好的语法形式的原因是它在子类中的显示方式:

class PersonalChefUK(Personal_Chef):
    @at_time('evening')
    def cook():
        print("Cooking supper")

在子类级别编写的代码非常少,不需要了解基类实现/数据结构,也不需要任何对 super() 的调用来传递其他场景。因此,如果有很多人在编写派生类,我们希望将复杂性打包并隐藏在基类中,并使他们很难破坏功能,那么这可能会很好。

但是,我尝试了几种不同的方法来实现这一点,但都陷入了困境。不过,我对装饰器还很陌生,所以可能错过了一些重要的东西。有什么建议/意见吗?

python methods polymorphism decorator
1个回答
0
投票

您可以创建一个充当函数装饰器的描述符,在调用时,将装饰函数存储在函数完全限定名称到字典的映射中,该字典将一天中的时间映射到操作函数,以便在调用描述符的 getter 函数时,它可以返回一个包装函数,该函数在 MRO 中查找第一个类,该类具有为一天中的给定时间定义的操作函数名称并调用它:

class at_time:
    actions = {}

    def __init__(self, time_of_day):
        self.time_of_day = time_of_day

    def __call__(self, func):
        self.actions.setdefault(func.__qualname__, {})[self.time_of_day] = func
        return self

    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, obj, objtype):
        def wrapper(time_of_day):
            for cls in objtype.__mro__:
                if func := self.actions.get(
                    '.'.join((cls.__qualname__, self.name)), {}).get(time_of_day):
                    return func(obj)
            raise ValueError('No {self.name} found in the {time_of_day} time')
        return wrapper

这样:

class PersonalChef():
    @at_time('morning')
    def cook(self):
        print("Cooking breakfast")

    @at_time('evening')
    def cook(self):
        print("Cooking dinner")

class PersonalChefUK(PersonalChef):
    @at_time('evening')
    def cook(self):
        print("Cooking supper")

PersonalChef().cook('morning')
PersonalChef().cook('evening')
PersonalChefUK().cook('evening')

输出:

Cooking breakfast
Cooking dinner
Cooking supper

演示:https://ideone.com/fbIoqf

© www.soinside.com 2019 - 2024. All rights reserved.