Подтвердить что ты не робот

Переопределение свойств в python

Итак, я пытаюсь найти лучший (самый элегантный с наименьшим количеством кода) способ разрешить переопределение определенных функций свойства (например, только getter, только setter и т.д.) в python. Я являюсь поклонником следующего способа выполнения свойств из-за того, что все их методы инкапсулированы в один и тот же отступы блок кода (проще видеть, где функции, связанные с одним свойством, останавливаются и функции, связанные с следующее начало):

@apply
def foo():
    """A foobar"""
    def fget(self):
        return self._foo
    def fset(self, val):
        self._foo = val
    return property(**locals())

Однако, если я хочу наследовать от класса, который определяет свойства таким образом, а затем, скажем, переопределить функцию set t1 > , это кажется сложным. Я сделал несколько поисков, и большинство ответов, которые я нашел, заключались в том, чтобы определить отдельные функции в базовом классе (например, getFoo и setFoo), явно создать определение свойств из них (например, foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())), а затем переопределить getFoo, setFoo и delFoo по мере необходимости.

Мне не нравится это решение, потому что это означает, что я должен определить lambas для каждого отдельного свойства, а затем выписать каждый вызов функции (когда раньше я мог только что сделать property(**locals())). Я также не получаю инкапсуляцию, которую я изначально.

В идеале то, что я хотел бы сделать, было бы примерно так:

class A(object):
    def __init__(self):
        self.foo = 8
    @apply
    def foo():
        """A foobar"""
        def fget(self):
            return self._foo
        def fset(self, val):
            self._foo = val
        return property(**locals())

class ATimesTwo(A):
    @some_decorator
    def foo():
        def fset(self, val):
            self._foo = val * 2
        return something

И тогда результат будет выглядеть примерно так:

>>> a = A()
>>> a.foo
8
>>> b = ATimesTwo()
>>> b.foo
16

В принципе, ATimesTwo наследует функцию getter от A, но переопределяет функцию setter. Кто-нибудь знает способ сделать это (таким образом, который похож на приведенный выше пример)? Какая функция будет выглядеть как some_decorator и что должна вернуть функция foo?

4b9b3361

Ответ 1

Я уверен, что вы слышали это раньше, но apply устарел в течение восьми лет, так как Python 2.3. Не используйте его. Ваше использование locals() также противоречит Zen Python - явное лучше, чем неявное. Если вам действительно нравится увеличенный отступ, нет необходимости создавать объект throwaway, просто выполните

if True:
    @property
    def foo(self):
        return self._foo
    @foo.setter
    def foo(self, val):
        self._foo = val

Что не злоупотребляет locals, используйте apply, требует создания дополнительного объекта или потребуется строка с foo = foo(), что затрудняет просмотр конца блока. Он работает так же хорошо для вашего старомодного способа использования property - просто выполните foo = property(fget, fset) как обычно.

Если вы хотите переопределить свойство в произвольном подклассе, вы можете использовать рецепт, подобный этому.

Если подкласс знает, где было определено свойство, просто выполните:

class ATimesTwo(A):
    @A.foo.setter
    def foo(self, val):
        self._foo = val * 2

Ответ 2

Python docs на декораторе property предлагает следующую идиому:

class C(object):
    def __init__(self):
        self._x = None
    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, value):
        self._x = value
    @x.deleter
    def x(self):
        del self._x

И тогда подклассы могут переопределить один сеттер/получатель следующим образом:

class C2(C):
    @C.x.getter
    def x(self):
        return self._x * -1

Это немного бородавчато, потому что для переопределения нескольких методов вам требуется сделать что-то вроде:

class C3(C):
    @C.x.getter
    def x(self):
        return self._x * -1
    # C3 now has an x property with a modified getter
    # so modify its setter rather than C.x setter.
    @x.setter 
    def x(self, value):
        self._x = value * 2

Конечно, в момент, когда вы переопределяете getter, setter и deleter, вы, вероятно, можете просто переопределить свойство для C3.