Как сделать изменяемое свойство доступным только для чтения?

У меня есть два класса, один с переопределением "оператор на месте" (скажем, +=) и другой, который выставляет экземпляр первого через @property, (Примечание: это значительно упрощает мой действительный код до минимума, который воспроизводит проблему.)

class MyValue(object):
    def __init__(self, value):
        self.value = value

    def __iadd__(self, other):
        self.value += other
        return self

    def __repr__(self):
        return str(self.value)

class MyOwner(object):
    def __init__(self):
        self._what = MyValue(40)

    @property
    def what(self):
        return self._what

Теперь, когда я пытаюсь использовать этот оператор для открытого свойства:

>>> owner = MyOwner()
>>> owner.what += 2
AttributeError: can't set attribute

Из того, что я обнаружил, следует ожидать, так как он пытается установить свойство на owner, Есть ли какой-нибудь способ предотвратить установку свойства для нового объекта, в то же время позволяя мне (на месте) изменять объект, стоящий за ним, или это просто причуды языка?

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


В то же время я работал с этим методом, который делает то же самое.

class MyValue(object):
    # ... 

    def add(self, other):
        self.value += other

>>> owner = MyOwner()
>>> owner.what.add(2)
>>> print(owner.what)
42

1 ответ

Решение

Это причуда языка; object += value операция переводится в:

object = object.__iadd__(value)

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

В вашем случае object это также атрибут, поэтому выполняется следующее:

owner.what = owner.what.__iadd__(2)

Помимо избегания ссылок object.what здесь на левой стороне (как tmp = owner.what; tmp += 2), есть способ справиться с этим чисто.

Вы можете легко обнаружить, что присвоение свойству относится к тому же объекту и воротам этого:

class MyOwner(object):
    def __init__(self):
        self._what = MyValue(40)

    @property
    def what(self):
        return self._what

    @what.setter
    def what(self, newwhat):
        if newwhat is not self._what:
            raise AttributeError("can't set attribute")
        # ignore the remainder; the object is still the same
        # object *anyway*, so no actual assignment is needed

Демо-версия:

>>> owner = MyOwner()
>>> owner.what
40
>>> owner.what = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 24, in what
AttributeError: can't set attribute
>>> owner.what += 2
>>> owner.what
42
Другие вопросы по тегам