Особенность переопределенного установщика свойств в подклассе - PullRequest
2 голосов
/ 14 февраля 2012

Итак, у меня есть это:

class Parent(object):

    def __init__(self, val):
        print 'enter Base init'
        self._set_x(val)
        print 'leave Base init'

    def _get_x(self):
        return self._x

    def _set_x(self, val):
        print 'enter Base _set_x'
        self._x = val
        print 'leave Base _set_x'

    x = property(_get_x, _set_x)

class Child(Parent):

    def _set_x(self, val):
        print 'enter Child _set_x'
        y = val * 2
        super(Child, self)._set_x(y)
        print 'leave Child _set_x'

child = Child(5)
num = child.x
child.x = 5
print num == child.x

И когда я запускаю его, я получаю это:

enter Base init
enter Child _set_x
enter Base _set_x
leave Base _set_x
leave Child _set_x
leave Base init
enter Base _set_x
leave Base _set_x
False

Я читал вокруг, и люди говорят, что переопределение не должно работать, но мой вопрос: почему здесь это кажущееся несоответствие? Установщик подкласса вызывается при вызове из init, но когда вы позже воздействуете на уже инициализированный объект, он вызывает установщик базы. Может кто-нибудь объяснить, что здесь происходит?

Ответы [ 4 ]

5 голосов
/ 14 февраля 2012

Потому что вы называете это непоследовательно - один раз напрямую, а один раз через свойство. Измените self._set_x(x) в Parent.__init__ на self.x = x, и вы увидите, что Child._set_x никогда не вызывается. Чтобы переопределить сеттер в дочернем классе, вы можете использовать property.setter в качестве декоратора:

class Child(Parent):
    @Parent.x.setter
    def x(self, arg):
        super()._set_x(arg)

Или добавить уровень косвенности к свойству:

class Parent(object):
    # ...
    x = property(
        lambda self:    self._get_x(),
        lambda self, x: self._set_x(x)
    )

Переопределение не работает напрямую, потому что свойство хранит конкретные объекты методов (Parent._get_x и Parent._set_x) и не ищет их снова, даже если тип объекта изменяется - код, который работает одинаково независимо. Поиск сил косвенного действия в динамическом типе self, который допускает переопределение.

3 голосов
/ 14 февраля 2012

Нет противоречий. Метод __init__ явно вызывает self._set_x через поиск атрибутов. self относится к объекту Child здесь, и поскольку Child определяет _set_x, а класс Child является первым в Порядке разрешения метода (MRO), его версия _set_x - это версия, которая вызывается.

Но свойство x определено внутри Parent. Дочерние элементы еще не задействованы, поэтому версии _set_x и _get_x, переданные property, являются версиями, определенными в Parent. Теперь, когда осуществляется доступ к атрибуту x Child, Python сначала ищет x в классе Child. Но он не находит его, потому что Child не определяет его. Затем он переходит к следующему классу в MRO: Parent. Он находит x там и использует его, как определено в Parent.

2 голосов
/ 14 февраля 2012
x = property(_get_x, _set_x)

Здесь x становится свойством с геттером Parent._get_x и сеттером Parent._set_x.

Если вы добавите строку x = property(Parent._get_x, _set_x) в класс Child, свойство будет переопределенои будет работать, как вы ожидали.

0 голосов
/ 14 февраля 2012

Думайте о методах как об объектах.Свойство x в вашем родительском классе было привязано к объектам метода _get_x / _set_x родительского класса.Но в вашем классе Child вы вызываете собственный объект класса _set_x этого класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...