Лучшая практика для определения класса, который вычисляет атрибуты по порядку при инициализации - PullRequest
1 голос
/ 28 февраля 2020

Я хотел бы определить класс, который выполняет что-то вроде:

Class computer():
    def __init__(self, x):
        # compute first the 'helper' properties
        self.prop1 = self.compute_prop1(x)
        self.prop2 = self.compute_prop2(x)
        # then compute the property that depends on 'helpers'
        self.prop3 = self.compute_prop3(x)

    def compute_prop1(self, x):
        return x
    def compute_prop2(self, x):
        return x*x
    def compute_prop3(self, x):
        return self.prop1 + self.prop2

Затем, когда я инициализирую экземпляр, я получаю все свойства, вычисленные по порядку (сначала помощники, затем все, что зависит от помощников позже) :

>>> computer = Computer(3)
>>> computer.__dict__
{'prop1': 3, 'prop2': 9, 'prop3': 12}

Тем не менее, я думаю, что лучше написать этот код, например, с использованием декораторов. Не могли бы вы дать мне несколько советов? Спасибо!

Ответы [ 2 ]

3 голосов
/ 28 февраля 2020

Вот ваш класс, использующий вместо этого свойства (с добавленным методом для возврата каждого свойства):

Class PropertyComputer:
    def __init__(self, x):
        self._x = x

    @property
    def prop1(self):
        return self._x

    @property
    def prop2(self):
        return self._x * self._x

    @property
    def prop3(self):
        return self.prop1 + self.prop2

    def get_props(self):
        return self.prop1, self.prop2, self.prop3

По дизайну я считаю, что это лучше, потому что:

  • хранение x в качестве переменной экземпляра имеет больше смысла: смысл использования объектов состоит в том, чтобы избежать необходимости передавать переменные, особенно те, которые сам объект может отслеживать;
  • назначение атрибута и его соответствующие вычисления связаны друг с другом в каждом методе, декорированном свойством: нам никогда не придется думать, в чем проблема - в методе init (где вы определяете атрибут) или в методе compute (где выложена логика c для вычисления атрибута ).

Обратите внимание, что концепция «сначала вычислить помощников, а затем свойства в зависимости от них» на самом деле не применима к этому коду: нам нужно только оценить prop3, если / когда нам действительно нужно Это. Если мы никогда не обращаемся к нему, нам никогда не нужно его вычислять.

«Плохой» побочный эффект использования свойств по сравнению с вашим примером состоит в том, что эти свойства нигде не «хранятся» (поэтому я и добавил последний метод):

c = PropertyComputer(x=2)
c.__dict__  # outputs {'_x': 2}

Также обратите внимание, что при использовании декораторов атрибуты вычисляются на лету при каждом обращении к ним вместо одного раза в методе init. Таким образом, методы, украшенные свойствами, работают как методы, но к ним обращаются как к атрибутам (суть их использования):

c = PropertyComputer(x=2)
c.prop1  # outputs 2
c._x = 10
c.prop1  # outputs 10

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

0 голосов
/ 28 февраля 2020

Я думаю, что следующий будет самым простым способом избежать избыточности

class computer():
    def __init__(self, x):
        self.prop_dict = self.compute_prop_dict(x)

    def compute_prop_dict(self, x):
        prop1 = x
        prop2 = x*x
        return {'prop1': prop1, 'prop2': prop2, 'prop3': prop1 + prop2}

Так что все, что придет после создания экземпляра, может иметь доступ к этим помощникам через prop_dict

Но как сказал Брайан в качестве комментария, этот заказ является просто языковой спецификацией для Python 3.7

...