Перехват вызовов определенных функций с помощью родительских/дочерних классов?

Я пишу фреймворк, который позволяет нам перехватывать определенные вызовы функций, чтобы мы могли управлять ими (обеспечивать таймауты и повторные попытки, сохранять возвращаемые значения и т.д.). Сейчас я думаю о том, чтобы ввести абстрактный класс с методом, который вызывает абстрактный метод, и заставить пользователей/клиентов наследовать абстрактный класс и делать свои вызовы функций внутри абстрактного метода.

Конкретно, я пишу абстрактный класс Fruit, который ожидает от клиентов:

  1. Extend Fruit and override the eat_fruit method
  2. Invoke the eat method when actually wanting to use the overriden eat_fruit method

Причина, по которой я думаю сделать это, заключается в том, что мы хотим регистрировать количество вызовов eat_fruit

class Fruit:
    def eat(self, *args, **kwargs):
        self.eat_fruit(*args, **kwargs)
        
    def eat_fruit(self, *args, **kwargs):
        pass

class Apple(Fruit):
    def eat_fruit(self, *args, **kwargs):
        print("Eat Apple")
        
apple = Apple()
apple.eat()

Однако мы сталкиваемся с проблемой расширяемости. Допустим, клиенту нужно добавить входной параметр color в метод Apple eat_fruit, он может добавить его следующим образом:

class Fruit:
    def eat(self, *args, **kwargs):
        self.eat_fruit(*args, **kwargs)
        
    def eat_fruit(self, *args, **kwargs):
        pass

class Apple(Fruit):
    def eat_fruit(self, color, *args, **kwargs):
        print("Eat "+ color + " Apple")
        
apple = Apple()
apple.eat("Red")

Это выполнимо, но зависит от того, знают ли они, что метод Fruit eat передает *args и **kwargs непосредственно в метод eat_fruit, что кажется деталью реализации в методе eat, которая может быть изменена.

Мне интересно, есть ли лучшие способы добиться того же самого. В частности,

  1. Is it possible for them to invoke eat_fruit directly(rather than invoking the parent class's eat method and assume that the arguments will be forwarded to eat_fruit)? I can't think of a way for them to do that while still letting us keep track of eat_fruit invocations
  2. Is there a better way(rather than using parent-child classes) to intercept function invocations? The final goal of this framework is to intercept specific function invocations made by clients so that we can manage them(enforce timeouts and retries, persist return values etc.).

Спасибо! Джессика

По моему опыту, фреймворки и API решают эту проблему с помощью Event-driven architecture.

Хотя другие методы могут решить ваши проблемы, например, с помощью декораторов, если вы хотите полностью контролировать происходящее, вы можете рассмотреть эту архитектуру.

По сути, вместо непосредственного выполнения функций вы "запускаете" или "вызываете" "событие", которое связано с данной функцией, но которое также может быть связано с несколькими функциями. Пользователь может иметь свои собственные функции, а фреймворк может иметь свои собственные функции, при этом ни одна из сторон не должна знать слишком много о другой. Пользователь доволен тем, что его функции выполняются, а фреймворк доволен тем, что имеет контроль над любым событием, которое запускается или вызывается.

Однако это не маленькая функция, которую может иметь фреймворк, особенно если вы реализуете ее с нуля. Поэтому при рассмотрении возможности реализации этой функции в вашем фреймворке, стоит проверить, есть ли уже поддержка этой функции в используемом вами языке или доступна ли сторонняя библиотека, которая может предоставить эту функцию для вас.

Вернуться на верх