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(f'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('morning')
PersonalChefUK().cook('evening')