Este post é uma sequência deste outro post sobre os getters, vale apena conferi-lo antes.
Após entendermos melhor como funcionam as propriedades em Python e aplicarmos os getters em nossa classe Circulo, ficamos com a pendência de entender como podemos agora aplicarmos os setters. O principio é muito similar e simples. Para isso basta definirmos o método em questão e decorá-lo com @nome_da_propriedade.setter.
import math
class Circulo:
def __init__(self, raio):
self._raio = raio
@property
def raio(self):
return self._raio
@property
def area(self):
return self.raio ** 2 * math.pi
@property
def circunferencia(self):
return self.raio * 2 * math.pi
@raio.setter
def raio(self, value):
assert value > 0, 'O valor do raio deve ser maior que 0'
self._raio = value
c = Circulo(5)
print(c.area)
c.raio = -1
Aproveitei também para já colocar uma validação no método e passei a utilizá-lo no método __init__. A criação é muito simples e prática, apesar de a API do setter não ser tão intuitiva, seu funcionamento é tão simples como a criação dos getters. Por se tratar um método podemos inclusive fazer outras computações nele, como por exemplo criar um setter para a área:
import math
class Circulo:
def __init__(self, raio):
self.raio = raio
@property
def raio(self):
return self._raio
@property
def area(self):
return self.raio ** 2 * math.pi
@property
def circunferencia(self):
return self.raio * 2 * math.pi
@raio.setter
def raio(self, value):
assert value > 0, 'O valor do raio deve ser maior que 0'
self._raio = value
@area.setter
def area(self, value):
assert value > 0, 'A área deve ser maior que zero'
self.raio = math.sqrt(value / math.pi)
c = Circulo(5)
print(c.area)
c.area = 10
print(c.raio)
Como podemos ver, definimos a partir da área o novo raio do circulo, também colocamos uma validação para os valores serem maiores que zero e conseguimos assim ter uma classe completa.
Como em Python também podemos remover alguma propriedade usando a palavra reservada del, também podemos definir métodos que lidam com este comportamento. Como no exemplo abaixo:
import math
class Circulo:
def __init__(self, raio, cor=None):
self.raio = raio
self._cor = cor
# Omitindo outros métodos
@property
def cor(self):
return self._cor
@cor.setter
def cor(self, value):
self._cor = value
@cor.deleter
def cor(self):
self._cor = "transparente"
c = Circulo(5, "vermelho")
del c.cor
print(c.cor)
Fazer esse tratamento para o del é algo muito interessante, podemos através deste método fazer muito mais coisas que apenas limpar uma propriedade, como por exemplo, realizar uma limpeza de outro recursos associados a propriedade (algo como um delete em cascata).
A principal vantagem desse tratamento de propriedades no Python é o fato de podermos adicionar as validações posteriormente. Hoje em dia existem muitos outros tipos no Python para ajudar a carregar valores, como as dataclasses e as namedtuples, que valem muito apena serem estudadas, mas com certeza as propriedades ainda são muito utilizadas.