Jeito pythonico de definir setters e getters – python propriedade getters-setters

Pergunta:


Pesquisando na internet eu vejo que é altamente recomendável não utilizar Setter e Getters em classes Python, porém, não consigo determinar uma forma de acessar valores privados sem utiliza-los. Existem diversas recomendações sobre usar propriedades, porém não consigo entender a vantagem pois não vejo a possibilidade de determinar um método para todos os atributos, e ainda sim teria de determinar um método para cada (ou eu não estou entendendo seu uso). Exemplo:

class Pessoa(object):
    def __init__(self, nome, idade, salario)
        self.__nome = nome
        self.__idade = idade
        self.__salario = salario

    def nomeGet(self):
        return self.__nome

    def nomeSet(self, nome):
        self.__nome = nome

    def idadeGet(self):
        return self.__idade

    def idadeSet(self, idade):
        self.__idade = idade

    def salarioGet(self):
        return self.__salario

    def salarioSet(self, salario):
        self.__salario = salario

Sei que não é considerado uma boa prática no Python definir tantos métodos acessores, porém, como mencionei antes não tenho ideia de como evitar isto, a imagem que tenho utilizando propriedade seria assim da mesma forma, a diferença seria instanciar a property para cada atributo. Alguém poderia me explicar a melhor forma de trabalhar com mais de um atributo, ou porque seria melhor utilizar propriedades?

Autor da pergunta afonso.prog

Resposta jsbueno:

Vamos por partes:

Quando “toda a Internet” diz uma coisa e você não quer fazer o mesmo – quem você acha que está indo na contramã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

    @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 chaamda 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.

Existe exatamente essa pergunta no SO EN.

Se precisas mesmo de setters e getters, dever usá-los através de property, ex:

class Pessoa(object):
    def __init__(self, nome, idade, salario):
        self._nome = nome
        self._idade = idade
        self._salario = salario

    @property
    def nome(self):
        print('get do nome')
        return self._nome

    @nome.setter
    def nome(self, nome):
        print('set do nome', nome)
        self._nome = nome

    @property
    def idade(self):
        print('get da idade')
        return self._idade

    @idade.setter
    def idade(self, idade):
        print('set da idade', idade)
        self._idade = idade

    @property
    def salario(self):
        print('get do salario')
        return self._salario

    @salario.setter
    def salario(self, salario):
        print('set do salario', salario)
        self._salario = salario


pessoa = Pessoa('Miguel', 30, 50)
print(pessoa.__dict__) # valores iniciais
pessoa.nome = 'Afonso'
pessoa.idade = 20
pessoa.salario = 500
print(pessoa.nome)
print(pessoa.idade)
print(pessoa.salario)

Isto é util se quiseres que algum código seja executado (como por ex, os prints em cima que estão dentro dos métodos) quando fazes set, get, del em alguma propriedade.

Olhando para outras pesquisas e artigos escritos por pessoas que sabem mais que eu, concordo plenamente.

…just access the attribute itself, or, if you need code to be run every time the attribute is accessed or set, use properties.

Que traduzido é:

…acede logo ao próprio atributo, ou, se precisares que algum código seja executado sempre que o atributo seja acedido/definido (set/get) usa properties (o meu exemplo em cima)

E aqui está um exemplo, tendo em conta o teu código, do que eu faria (caso não precisasse que nada extra fosse executado num get/set/del), tento manter o mais simples possível:

class Pessoa(object):
    def __init__(self, nome, idade, salario):
        self.nome = nome
        self.idade = idade
        self.salario = salario

pessoa = Pessoa('Miguel', 30, 50)
print(pessoa.__dict__) # valores iniciais
pessoa.nome = 'Afonso'
pessoa.idade = 20
pessoa.salario = 500
print(pessoa.__dict__) # novos valores
print(pessoa.nome)
print(pessoa.idade)
print(pessoa.salario)

Neste ultimo exemplo talvez adicionasse só os metodos para retorno, (def get_nome, …) por uma questão de legibilidade, mas não é de todo necessário/convenção.

“Beleza da simplicidade de Python, não a desperdices”

class Pessoa(object):

    def __init__(self, nome, idade, salario):
        self.__nome = nome
        self.__idade = idade
        self.__salario = salario

    def nomeGet(self):
        return self.__nome

    def nomeSet(self, nome):
        self.__nome = nome

    def idadeGet(self):
        return self.__idade

    def idadeSet(self, idade):
        self.__idade = idade

    def salarioGet(self):
        return self.__salario

    def salarioSet(self, salario):
        self.__salario = salario
print(Pessoa)        

Fonte

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *