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

O que utilizar dentro da classe? Atributo privado ou property?

Essa discussão sempre surge em outras linguagens. Como acessar atributos privados da classe dentro de outros métodos dessa mesma classe? A discussão segue em torno da performance versus integridade dos dados. Assim, uns defendem que dentro de deposita(self, valor), por exemplo, deve-se usar self.__saldo += valor enquanto outros defendem o acesso pelos "gets", como self.saldo += valor. Em Java seria algo como:

public void deposita(valor) {
    this.saldo += valor;
}

versus

public void deposita(valor) {
    this.setSaldo(this.getSaldo() + valor);
}

Qual seria a melhor prática?

4 respostas

Oi Franco, como você está? Espero que esteja bem ^-^

Antes de irmos à pergunta, vamos recapitular alguns pontos:

Em python nada é verdadeiramente privado, sempre haverá uma forma de acessarmos o atributo fora da classe, tanto no uso de um quanto no de dois undescores. Quando utilizamos o duplo undescore, o que ocorre é que o programa modifica o nome do atributo para que ele seja de difícil acesso do lado externo da classe, mas, mesmo assim é possível acessá-lo.

Suponha o atributo __nome de uma classe Animal. Para acessarmos esse atributo do lado externo da classe, podemos fazer: _Animal__nome, isso é chamado de desconfiguração de nomes (name mangling), onde a sintaxe é: um underscore seguido do nome da classe e em seguida, o duplo underscore seguido do nome do atributo, porém, isso tem a ver com proteção e não com segurança. Isso foi feito para evitar acessos acidentais e não contra ações maliciosas intencionais, pois, qualquer pessoa que saiba sobre como os atributos de duplo undercore são desconfigurados, poderá lê-los fora da classe, veja um exemplo:

class Animal:
  def __init__(self, nome):
    self.__nome = nome

cachorro = Animal("Bob")
cachorro._Animal__nome 

# Resultado
Bob

E não apenas lê-lo fora da classe, mas também, modificá-lo:

cachorro = Animal("Bob")
print(cachorro._Animal__nome)
cachorro._Animal__nome = "Pity"
print(cachorro._Animal__nome)

# Resultado
Bob
Pity

Na comunidade Python, é convenção o uso de um único undescore como prefixo para "proteger" o atributo e é algo bem respeitado, pois, quando avistamos uma variável precedida pelo undescore, sabemos que aquele atributo funciona como privado, apesar de não ser verdadeiramente assim, tal como na linguagem Java. Esta convenção é definida na própria documentação da linguagem. Geralmente utiliza-se o duplo undescore quando queremos nos aproximar aos conceitos de atributo privado presente em outras linguagens. A escolha de um ou de outro varia de programador para programador, mas, deve-se manter uma consistência, se utilizar um undescore, que o utilize sempre, se utilize o duplo, que o utilize sempre, para não ficar algo desorganizado no código.

Dito isso, então como acessar atributos privados da classe dentro de outros métodos dessa mesma classe?

Quanto ao acesso dele, tanto dentro quanto fora da classe, a maneira "pythônica" de se fazer é através de propertys, até mesmo na inicialização da classe, veja um exemplo:

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

  @property
  def nome(self):
    return self.__nome

  @nome.setter
  def nome(self, nome):
    print("Chamando o setter")
    self.__nome = nome

cachorro = Animal("Bob")

# Resultado
Chamando o setter

No exemplo acima, estamos utilizando a property de setter implicitamente no nosso método inicializador da classe e dentro do setter, criamos um atributo "privado" com duplo undescore. Essa é uma boa prática, trabalhar com propertys dentro da classe, uma vez que as mesmas já estão a disposição e por vezes, o property de setter pode até mesmo contar com validações. Como mostro abaixo:

class Quadrado:
  def __init__(self, lado):
    self.lado = lado

  @property
  def lado(self):
    return self.__lado

  @lado.setter
  def lado(self, lado):
    print("Chamando o setter")
    if lado > 3:
      self.__lado = lado
    else:
      raise ValueError("O valor do lado tem que ser maior que 3")

Espero ter esclarecido sua dúvida e qualquer coisa, vamos conversando :D

Grande abraço!

Não dará estouro de pilha com chamadas recursivas aqui?

 @property
  def nome(self):
    return nome

e aqui?

@property
  def lado(self):
    return lado

Não teria que retornar um __nome e um __lado neste caso por se tratar do próprio método de acesso ao atributo?

solução!

Isso, certinho. E também daria um erro dizendo que o atributo nome ou lado não foi definido, pois temos que preceder o atributo pelo self. No caso específico do retorno da property teremos que retornar o atributo em sua forma de criação, ou seja: __lado ou __nome, perdão ter esquecido desse detalhe no exemplo.

Qualquer coisa a gente vai se falando.

Ok. Grato.