Как сделать изменяемое свойство доступным только для чтения?
У меня есть два класса, один с переопределением "оператор на месте" (скажем, +=
) и другой, который выставляет экземпляр первого через @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