Питонический способ построения мультиметодного сеттера - PullRequest
1 голос
/ 28 июня 2019

Мы можем использовать @property для создания геттера и сеттера. Вот краткий пример того, как мы можем сделать это:

class A:

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

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 100:
            self.__x = 100
        else:
            self.__x = x

Мой случай кажется более сложным.

class A:

    def __init__(self, x):
        self.__x = x
        self.x1()
        self.x2()
        self.x3()

    def x1(self):
        self.__x1 = self.__x + 1
        return self.__x1

    def x2(self):
        self.__x2 = self.__x1 + 2
        return self.__x2

    def x3(self):
        self.__x3 = self.__x2 + 3
        return self.__x3


if __name__ == "__main__":
    a = A(3)
    print(a.x3)

Методы x1, x2 и x3 упрощены. Переменная self.__x3 устанавливается только один раз, когда вызывается метод __init__. Теперь мне нужен метод get для получения self.__x3 путем вызова a.x3. Как этого достичь питонским способом?

1 Ответ

2 голосов
/ 28 июня 2019

Попытка ответа, основанная на предположении, что вы хотите, чтобы переменные __x# изменялись только во время __init__, и никогда больше, но также хотели, чтобы средства доступа следовали по одному и тому же пути кода (возможно, потому что чтение также программно сложно):

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

class A:

    def __init__(self, x):
        self.__x = x
        # Access the property itself off the class, bypassing execution,
        # then call it directly with the non-default argument
        type(self).x1.fget(self, True)

    @property
    def x1(self, doset=False):
        if doset:
            self.__x1 = self.__x + 1
        return self.__x1

В качестве альтернативы, чтобы упростить использование в __init__, вы можете использовать отдельное имя для базовой функции вместо property для достижения того же эффекта.:

class A:

    def __init__(self, x):
        self.__x = x
        # Call the implementing function directly with the non-default argument
        self._x1(True)

    # Implementing function named with single underscore prefix to indicate it's
    # for internal/protected use only
    def _x1(self, doset=False):
        if doset:
            self.__x1 = self.__x + 1
        return self.__x1
    # Define property x1 based on x1 for outside use
    x1 = property(_x1)

Конечно, если у вас нет сложного пути получения, тогда реальное решение - полностью отделить _x1 от x1, где _x1 - это чистая вспомогательная функция для установки__init__, а x1 - чистый получатель:

class A:

    def __init__(self, x):
        self.__x = x
        # Call the init helper
        self._init_x1()

    # Implementing function named with single underscore prefix to indicate it's
    # for internal/protected use only
    def _init_x1(self):
        self.__x1 = self.__x + 1

    @property:
    def x1(self):
        return self.__x1

Для ясности, только последний из них - "Pythonic" в любом значимом смысле.Второй вариант имеет несколько ограниченных вариантов использования (когда у вас есть функция, которая требует существования, и она легко настраивается, но имеет разумный набор значений по умолчанию, которые может использовать property), но в этом случае обычно это функция, которая имеетобщественная полезность так же, как property.Вариант № 1 является наименее Pythonic, так как его неудобно использовать (необходимо повысить уровень до класса, извлечь член fget и явно передать self), и ясно дает понять, что за пределами ожидаемого варианта использования нет__init__ (потому что это такая боль, которую никто не побеспокоит).

...