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

Encapsulamento com Python

Olá, conforme mencionado na aula de encapsulamento com Python, para "tornar um atributo privado" utilizamos o seguinte padrão: __NOME_DA_VARIAVEL, como no exemplo abaixo:

class Perfil(object):
    def __init__(self):
        self.__curtidas = 0

    def curtir(self):
        self.__curtidas += 1

    def obter_curtidas(self):
        return self.__curtidas

Ao executar essa classe no terminal observei o seguinte comportamento:

>>> perfil = Perfil()
>>> perfil.obter_curtidas()
0
>>> perfil.curtir()
>>> perfil.curtir()
>>> perfil.obter_curtidas()
2
>>> perfil.__curtidas = 100
>>> perfil.__curtidas
100
>>> perfil.obter_curtidas()
2

Quando eu utilizo perfil.__curtidas = 100 eu estou criando um novo atributo com o mesmo nome que deveria permanecer oculto para o desenvolvedor. Sendo assim eu passo a ter o atributo __curtidas dentro da classe, que por baixo dos panos está sendo referenciado pelo Python por um NOME_ALEATÓRIO, e um atributo dinâmico __curtidas, criado em tempo de execução.

Isso além de complicar o entendimento do código (principalmente se considerarmos códigos maiores), de certa forma também não quebra o encapsulamento?

Agradeço se alguém puder ajudar.

2 respostas
solução!

Olá, Giovanni! Sua pergunta é muito boa e importante, afinal temos algumas peculiaridades no funcionamento do paradigma de Orientação a Objetos na linguagem Python.

Se você vem de uma linguagem completamente voltada à orientação a objetos, como é Java, por exemplo, provavelmente está acostumado a algumas regras e restrições diferentes do que as que o Python nos provide. Para começar, digo logo - não existem atributos privados no Python! Ou melhor, ao menos como normalmente conhecemos.

Deixa eu explicar: Por boas práticas, quando trabalhamos com orientação a objetos no Python, é comum indicarmos que um atributo de uma classe deve ser privado, isto é, não deve ser mexido por outras classes, com um underline (_) na frente do nome da variável, dessa forma:

class Perfil(object):
    def __init__(self):
        self._curtidas = 0

    def curtir(self):
        self._curtidas += 1

    def obter_curtidas(self):
        return self._curtidas

Como você deve imaginar, isso não restringe acesso nenhum ao atributo, mas apenas indica ao desenvolvedor que não é recomendável modificar este atributo fora da classe:

>>> perfil = Perfil()
>>> perfil.curtir()
>>> perfil.obter_curtidas()
1
>>> perfil._curtidas = 5
>>> perfil.obter_curtidas()
5

Certo! Mas também temos o que foi visto no curso, que foi a tentativa de tornar o atributo privado com os dois underlines (__) na frente do nome da variável. Como já testamos, realmente não é possível acessar essa variável do jeito que esperaríamos que funcionasse:

>>> perfil = Perfil()
>>> perfil.curtir()
>>> perfil.obter_curtidas()
1
>>> perfil.__curtidas = 5
>>> perfil.obter_curtidas()
1

Ok! Mas será que realmente não temos como acessar o atributo __curtidas fora da classe? Olha isso:

>>> perfil = Perfil()
>>> perfil.curtir()
>>> perfil.obter_curtidas()
1
>>> perfil._Perfil__curtidas = 5
>>> perfil.obter_curtidas()
5

Conseguimos acessar o atributo __curtidas por fora da classe! O que acontece é que o Python, quando colocamos __ na frente de um nome de um atributo de uma classe, apenas renomeia esse atributo automaticamente de __atributo para _Classe__atributo, como fez de __curtidas para _Perfil__curtidas.

Isso, de fato, quebra o encapsulamento que conhecemos em linguagens como Java. A questão é que, no Python, a filosofia Pythônica é mais importante que o encapsulamento padrão. Encapsulamento existe no Python (é importante entender isso!), mas a linguagem não vai te proibir, isto é, impedir de tomar suas próprias decisões. Uma frase muito comum no mundo Python é we are all consenting adults here, ou, em uma tradução ao pé da letra, somos todos adultos com consentimento aqui. Podemos (e, na verdade, devemos!) indicar quais são as preferências para uso e continuação de nosso código, ou seja, se queremos, por exemplo, que um atributo seja acessado por fora de sua classe, mas realmente precisamos proibir o desenvolvedor de tomar suas próprias decisões? A filosofia Pythônica diz que não!

Enfim, para que serve, então, o comportamento dos atributos que começam com dois underlines (__)? De certa forma, como vimos, ele esconde o atributo, funcionando como encapsulamento. Além disso, é perfeito para o ponto que você indicou como problema: sobreposição de nomes de variáveis. Se precisarmos, ou melhor, querermos ter dois atributos com o "mesmo" nome, podemos usar esse comportamento ao nosso favor, como você mesmo demonstrou, já que o atributo __curtidas declarado dentro da classe (que é renomeado para _Perfil__curtidas é diferente do atributo __curtidas declarado fora da classe (que fica com o nome __curtidas). Como você mesmo indicou, temos que tomar cuidado com isso, para não criarmos um código confuso, mas, na verdade, isso não quebra o encapsulamento, afinal o atributo __curtidas definido dentro da classe continua não sendo acessado por fora dela.

Deu pra entender mais ou menos o que eu quis dizer, Giovanni, hehe? Dá um feedback aqui, pra se algo tiver ficado confuso!

Outra coisa, se você se interessa mais pela filosofia e as ideias por trás do Python, recomendo duas leituras para você. Uma é o PEP 8, que é um guia de estilo para código Python e acaba tratando bastante da filosofia pythônica.

A outra leitura, também muito interessante, é, na verdade, um easter egg no Python. Não vou te falar o resultado, mas tente, no console Python, digitar o seguinte código:

>>> import this

e diga o que acha!

Abraços e bons estudos!

Obrigado Yan, com essa explicação ficou bem claro agora!

Um abraço cara, Valeu!!!!