Где поместить разделение между объектом с состоянием и вычислением без состояния в Python?

1

Какой из следующих фрагментов кода является наиболее "питоническим"? В этом примере вычисление тривиально, но в реальной жизни его можно считать сложным.

class A(object):
    """Freely mix state and calcs - no good I presume"""
    def __init__(self, state):
        self.state = state

    def calc_with_state(self, x):
        return (self.state + x)**2

или

class B(object):
    """Separate state from calc by a static method"""
    @staticmethod
    def inner_calc(u, v):
        return (u + v)**2

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

    def calc_with_state(self, x):
        return B.inner_calc(self.state, x)

или

class C(object):
    """Break out the calculation in a free function"""
    def __init__(self, state):
        self.state = state

    def calc_with_state(self, x):
        return outer_calc(self.state, x)

def outer_calc(u, v):
    return (u + v)**2
Теги:

5 ответов

4
Лучший ответ

Как написано, A, длинным ударом. Проблема, довольно просто,

Плоский лучше, чем вложенный.

Посмотрите: разделение состояния от вычислений является хорошим конструктивным принципом, но это не значит, что вы думаете, по крайней мере, не то, что я могу предположить, вы думаете из этого примера. Мы хотим убедиться, что состояние не изменится, чтобы завершить вычисления, если это состояние не будет повторно инициализировано при следующем вычислении. Если состояние доступно только для чтения в отношении какого-либо конкретного вычисления, нет стилистического принуждения к перенаправлению вокруг него, чтобы вы не читали его напрямую. То есть, если расчет и состояние достаточно сложны, чтобы требовать отдельного модульного тестирования. Тогда, во что бы то ни стало, предпочтительнее B или C, но только в том случае, если на самом деле гораздо проще создавать значения для u, чем создавать экземпляры A.

  • 0
    Большинство из нас здесь сказали то же самое. Ты побил нас цитатой из PEP20. +1!
  • 0
    Да, но я начинаю сомневаться, является ли это лучшим приложением на тот момент, так как я представляю себе, что обычно это понимают как применяемые к структуре данных, а не к структуре кода. Я мог бы пойти с «Простым лучше, чем сложным» и высказал аналогичное, но более смутное замечание.
Показать ещё 1 комментарий
1

Я думаю, это зависит от вашего конкретного проекта. Является ли cal_with_state применимым только к этому конкретному классу или этот метод должен использоваться для разных объектов? Разделяют ли разные классы?

В любом из этих подходов нет ничего более или менее питонического, используйте тот, который удовлетворит ваш проект. DRY находится за пределами питоничности.

  • 0
    Кажется, мы говорили примерно одно и то же в одно и то же время.
0

Факторинг расчета, будь то статический или глобальный метод, не дает никакой пользы в отношении осведомленности государства. Единственное небольшое преимущество заключается в том, что код явно показывает, какие из свойств объекта stateful учитываются при расчете (показать на уровне вызова функции, а не читать в логике метода в классе A)

Могут быть другие преимущества для внедрения методов безстоящих (статических, глобальных) или экземпляров:

  • повторное использование
  • читаемость и управление кодом в целом

но, как сказано, эти конструкции не помогают в отношении управления состоянием per-se. Подход A представляется вполне законным (и пифоническим) для меня. Действительно, Дэвид Бергер избил нас, напоминая нам, что...

Плоский лучше, чем вложенный!

.
0

Лично я бы сказал, что это зависит от возобновляемости. Если вычисление является тем, которое может использоваться с другими объектами (как это и должно быть), то третий пример является наиболее многоразовым. Если расчет полностью привязан к объекту или никогда не будет повторно использован, то первый пример. Я бы сказал, что второй пример в большинстве случаев неверен.

0

Что не так с A? Он выполняет расчет только в одном месте и не изменяет состояние, поэтому calc_with_state - это метод, который не меняет состояние, так что это "простое" поведение. Если состояние было сложным, оно останавливает большое количество параметров, передаваемых в функцию вычисления.

Однако, если вычисление может быть записано с учетом состояния как параметра (как в C), вы можете разделить расчет. преимущества здесь включают возможность сбрасывать этот расчет на данные, не входящие в класс C, а также вы можете использовать эту функцию в качестве данных, переданных в C, поэтому calc_with_state может быть вызван для вызова различных функций вычисления.

Я не могу видеть, что B имеет какие-либо преимущества, поскольку inner_calc не использует класс и поэтому может быть свободной функцией.

Поэтому я, скорее всего, напишу его как A, а затем, если вы захотите повторно использовать вычисление, сделайте класс использующим разные вычисления или просто если код вычисления станет слишком большим, вы можете реорганизовать в класс C

Ещё вопросы

Сообщество Overcoder
Наверх
Меню