Quero refatorar uma grande parte do meu código em um descritor genérico para acesso de atributo somente leitura. O seguinte é um exemplo de implementação baseada em propriedade
class A:
def __init__(self, n):
self._n = n
self._top = -1
@property
def n(self):
return self._n
@property
def top(self):
return self._top
def increase(self):
self._top += 1
você vê que eu posso inicializar minha classe A e aumentar, self._top
mas não deixar o usuário definir a.top
omitindo o método definidor de propriedade
a = A(7)
a.top
Out[25]: -1
a.increase()
a.top
Out[27]: 0
se eu fizer isso a.top = 4
, vai me dar um erro
AttributeError: property 'top' of 'A' object has no setter
o que é esperado. Agora, quero refatorar essa lógica em um descritor
class ReadOnly:
def __init__(self):
self._name = None
def __set_name__(self, owner, name):
self._name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self._name]
def __set__(self, instance, value):
raise AttributeError("Can't set attribute")
def __delete__(self, instance):
raise AttributeError("Can't delete attribute")
class A:
n = ReadOnly()
top = ReadOnly()
def __init__(self, n):
self.n = n
self.top = -1
def increase(self):
self.top += 1
Bem, isso não funciona. Não consigo nem inicializar A
mais a classe, porque __init__
ela define n e top imediatamente e impede minha inicialização.
Como escrever essa lógica da propriedade no descritor?
PS
Obrigado @chepner por esta solução. É isso que estou procurando. Eu fiz funcionar. Uma última coisa, se eu tiver um atributo é uma lista, digamos
class Stack:
S = ReadOnly()
n = ReadOnly()
top = ReadOnly()
def __init__(self, n):
self._S = [None] * n
self._n = n
self._top = -1 # python offset 1
Agora não posso mais alterar self.top
>>> s = Stack(4)
>>> s.S
[None, None, None, None]
Nem eu posso mudar isso
>>> s.S = [1, 3] # not allowed anymore. Great!
But I can still change an element in the list
>>> s.S[3] = 3
[None, None, None, 3]
Como posso evitar alterações nos elementos da lista?