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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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.