Solucionado (ver solução)
Solucionado
(ver solução)
2
respostas

Vantagens do uso do decorator @atributo.setter no python

Qual é a vantagem em utilizar este decorator no Python? Não seria mais fácil se o atributo fosse criado sem "__" antes do nome?

Me desculpem se a minha pergunta parecer ridiíula rsrs... Estudei orientação a objetos na faculdade mas minha experiência profissional é com linguagens voltadas ao paradigma procedural.

Agradeço se alguém puder me explicar a importância de usar o getters e setters assim no Python, já que a linguagem não suporta atributos privados.

2 respostas
solução!

Em Python não há o costume de se usar getters e setters, por que tem que se pensar de uma forma diferente. Quando você fala em "atributos privados" - eles são privados para "quem"?

A ideia do encapsulamento em OO é que pessoas usando sua classe e métodos púbico não precisem se preocupar com os estados privados, nem devam tentar mexer neles diretamente. Isso facilita que na hora de desenvolver a classe você não precise se preocupar com "e se alguém deixar esse atributo inconsistente" entre duas chamadas de métodos, e para quem está usando não precisa se preocupar com "se eu alterar esse valor, será que quebro alguma coisa no funcionamento do objeto"?

O objetivo de atributos privados NÃO É, por exemplo, evitar que alguém que use sua classe possa ler algum valor que você considera privado por questões de segurança para se proteger de algo como um "programador mal intencionado" que esteja fazendo uso de sua classe.

Como linguagens como Java e C++ são definidas, dá a impressão de que atributos e métodos privados possam oferecer segurança contra um programador mal intencionado que esteja usando sua classe. Essa segurança não é real - em ambas as linguagens é possível se acessar esses atributos privados - as vezes dando muitas voltas.

Em Python e outras linguagens dinâmicas, o uso de introspecção torna bem fácil achar e usar qualquer atributo marcado como privado (mesmo os prefixados com __)

Em suma: se alguém está escrevendo código que vai rodar no mesmo processo que sua classe com atributos privados - ele pode e deve poder acessar os dados. É diferente do caso de um programdor em um sistema diferente do seu que vai acessar seus dados por uma API. Nesse caso, os atributos privados simplesmente não são expostos na API. Em Python, em vez de tentar forçar atributos privados inacessíveis, é costume dizer que a linguagem é usada por "adultos que consentem" .

Então, a prática é prefixar atributos e métodos privados com um único _ : dessa forma quem for usar a classe sabe que não precisa mexer com esses atributos.

Agora, Python tem um mecanismo muito poderoso para acesso a atributos, que chamamos de "descriptor protocol" - isso é o que é usado internamente pelo property para permitir criar métodos - ele vai muito além do que o proeprt permite (basicamente você pode definir atributos numa classe que automaticamente tem getters e setters customizados - basta criar uma classe especial para esses atributos com os métodos get, set ou del - o property faz isso).

Dito isso, o property é um facilitador para você, ao querer ler ou escrever um atributo, executar algum código customizado que pode transformar ou validar o dado daquele atributo, quando ele for acessado (por exemplo, leia o valor de um banco de dados, faça uma formatação, validar o tipo do atributo, etc...).

Usar o property (ou getters e setters) para simplesmente guardar o valor como ele veio e devolve-lo como veio, não faz sentido - a não ser, por exemplo, que você queira que a classe seja thread-safe e use esse código para usar locks e semaphores na alteração dos atributos. E é por "não fazer sentido" que a internet recomenda que não se faça - simplesmente por que o seu programa vai ser exatamente o mesmo com ou sem os getters e setters -

Sem o property:

class Teste:
    def __init__(self, valor):
        self.valor = valor
Com o property:

class Teste:
    def __init__(self, nome):
         self.nome = nome

Com o property:

 @property
    def nome(self):
         # Este código é executado quando alguém for
         # ler o valor de self.nome
         return self._nome

    @nome.setter
    def nome(self, value):
         # este código é executado sempre que alguém fizer 
         # self.nome = value
         self._nome = value

E pronto - quem vai usar seu código, não vai usar uma chamada ao "setter" - a mágica do Python é que se você precisar colocar lógica customizada no getter e no setter, isso é completamente transparente para quem usa sua classe e seus atributos. Se nenhuma lógica extra é necessária para um dado atributo, não tem por que criar um property pra ele.

Welton, obrigado pela resposta! Realmente, só faz sentindo implementar os getters e setters se houver alguma lógica nos processos de armazenar ou retornar valor.

Interessante também saber que isso impacta em questões como thread-safe e semaphores. Este é outro assunto que ainda preciso me aprofundar. Vou ficar esperto quando eu estiver estudando isso!