Простой способ делегирования параметров с помощью большого количества методов

#python #software-design

Вопрос:

Имейте в виду, длинный вопрос.

У меня сложная проблема с дизайном в моем проекте на Python. Я попытаюсь упростить проблему, абстрагируясь от некоторых деталей. Я надеюсь, что моя проблема будет ясна. Если это не так, пожалуйста, попросите меня рассказать подробнее.


У меня есть несколько классов (давайте назовем Foo1 , Foo2 ,… Foon ), которые являются подклассами Foo класса. Foo имеет вызываемый метод score() . Каждый подкласс Foo переопределяет score метод.

Большинство методов оценки используют некоторые общие функции из другого модуля. Давайте назовем этот модуль lib . lib имеет некоторые функции, такие как f1(*args) , f2(*args) . Большинство из них очень просты, они просто вычисляют значение args с помощью простых математических операций, а затем возвращают это значение.

Пример метода оценки:

 def score():
    retval = 0
    retval  = lib.f1(some_arguments)
    retval  = lib.f2(some_other_arguments)
    return retval
 

Примерный lib.f метод:

 def f1(a, b, c):
    return a   b   c
 

Общий поток примерно такой:

Основная функция вызывает foo1.score() (где foo1-объект подкласса Foo ), который вызывает некоторые lib функции.

MainFunction -> foo1.score() -> lib.f1(*args) , lib.f2(*args)

Пока все хорошо.

В этот момент я понял, что некоторые lib.f функции (давайте назовем их lib.deep_f функциями) не так просты. Некоторым из них необходимо вызвать одну или несколько функций оценки объектов Foo1, Foo2,…. Пример:

 def deep_f1(foo_object):
    value = 0
    value  = foo_object.score()
    for other_foo_object in foo_object.neighbors:
        value  = other_foo_object.score()
    return value
 

Таким образом, поток может стать следующим:

MainFunction -> foo1.score() -> lib.deep_f1(*args) -> foo1.score() , foo2.score , … -> lib.deep_f1(*args) -> …

As you can see, there is a problem: a method calls another method that calls itself again. They may call each other infinitely. To solve this problem, I added a control parameter to lib.deep_f functions like deep_call . If it is a deep call (That is, if the function is called as a result of a deep_f function), I can return a predetermined value. So, I can change it as:

 def deep_f1(foo_object, *, deep_call=False):
    if deep_call:
        return 7
    value = 0
    value  = foo_object.score(deep_call=True)
    for other_foo_object in foo_object.neighbors:
        value  = other_foo_object.score(deep_call=True)
    return value
 

Also, I need to change all score methods that uses lib.deep_f functions, to delegate the deep_call parameter. So, the they will be similar to:

 def score(deep_call=False):
    retval = 0
    retval  = lib.deep_f1(self, deep_call=deep_call)
    retval  = lib.f1(some_arguments)
    retval  = lib.deep_f2(some_other_arguments, deep_call=deep_call)

    return retval
 

If I do the mentioned modifications, it will work as expected. The problem is that the number of Foo subclasses is very large. I need to add deep_call parameter to all of them. Also, I’ll add new Foo subclasses in the future. I will need to make these modifications on those classes as well. I think, there must be a better way. I guess, It may be done with Python context structures but I couldn’t find a solution. I imagine something as follows:

 def deep_f1(foo_object, *, deep_call=False):
    if deep_call:
        return 7
    value = 0
    with context:  # All deep_f function in this context should be called with deep_call=True
        value  = foo_object.score()
        for other_foo_object in foo_object.neighbors:
            value  = other_foo_object.score()
    return value
 

Но любое решение, которое избавит меня от изменения всех score() методов, приемлемо. Спасибо.