假设我们有一堂课如下:
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() 的调用来传递其他场景。因此,如果有很多人在编写派生类,我们希望将复杂性打包并隐藏在基类中,并使他们很难破坏功能,那么这可能会很好。
但是,我尝试了几种不同的方法来实现这一点,但都陷入了困境。不过,我对装饰器还很陌生,所以可能错过了一些重要的东西。有什么建议/意见吗?
您可以创建一个充当函数装饰器的描述符,在调用时,将装饰函数存储在函数完全限定名称到字典的映射中,该字典将一天中的时间映射到操作函数,以便在调用描述符的 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