Quando vemos uma classe em Python escrita com metodos getters e setters, logo percebemos que o programador ainda nao "pegou o jeito" de OOP em Python. Getters e setters sao muito comuns em outras linguagens de programacao porque os atributos normalmente sao declarados com escopo privado. Ou seja, quem usa a classe nao tem acesso aos atributos do objeto. Por isso criaram os metodos `get_...()` e `set_...()` para "violar" o encapsulamento dos membros privados.Encapsulamento e uma coisa muito boa. Devemos usa-lo sempre que for util. Alias, talvez as classes sejam uma das melhores formas de colocarmos o encapsulamento em pratica.Python, claro, encoraja encapsulamento, mas de outra forma. Para comecar, todos os membros de uma classe -- metodos e atributos -- tem visibilidade publica. Isso mesmo. Nada em um objeto fica escondido.Definimos metodos e dados privados usando uma convencao: um underline no inicio do nome. Por exemplo, `_codigo` ao inves de `codigo`. Parece estranho a principio, mas funciona muito bem. Afinal, se alguem quiser usar indevidamente um metodo ou um atributo da classe, estara sabotando o proprio programa. E o programa dele que vai dar errado!Vou dar uma pausa aqui para justificar essa opcao de projeto do Python. Nao podemos esquecer o contexto de uso de uma classe. Uma classe nunca sera usada por um usuario final diretamente. Ela sempre sera usada por um programa, escrito por um programador. Quem usa-la, sabera o que esta fazendo. Se nao souber, vai ver os erros quando estiver testando o codigo e vai corrigir.Quando vemos que tudo pode ser publico em uma classe, passamos a gostar da simplicidade. De fato, quase sempre comecamos a construir uma classe com todos os metodos e atributos sem o underline no inicio e so inserimos o underline ali quando fica claro o uso privado desse membro.Isso pode nos levar a um problema: o cliente da classe pode ficar sabendo demais sobre a implementacao e isso nao e encapsulamento. Abordaremos a solucao para esse problema nos exemplos abaixo.Agora vamos comecar a analisar um exemplo usando uma classe chamada `Pessoa`. Eu preciso de uma classe que guarde nome e sobrenome de uma pessoa. Mas tambem quero pegar o nome completo dela. Vamos aos codigos.Na abordagem convencional, fariamos assim: class Pessoa: def __init__(self, nome, sobrenome): self._nome = nome self._sobrenome = sobrenome def get_nome(self): return self._nome def set_nome(self, value): self._nome = value def get_sobrenome(self): return self._sobrenome def set_sobrenome(self, value): self._sobrenome = value def get_nome_completo(self): return "%s %s" % (self._nome, self._sobrenome)Temos getters, setters e um metodo especifico para pegar o nome completo: `get_nome_completo()`.Como em Python tudo e publico, essa classe poderia ser simplificada assim: class Pessoa: def __init__(self, nome, sobrenome): self.nome = nome self.sobrenome = sobrenome def get_nome_completo(self): return "%s %s" % (self.nome, self.sobrenome)Quando eu precisar pegar o nome da pessoa, simplesmente uso `pessoa.nome`. Quando eu quiser pegar o nome completo, uso `pessoa.get_nome_completo()`.Talvez nao seja obvio, mas um detalhe de implementacao da classe `Pessoa` esta exposto. E nao e o atributo `nome`. E o metodo `get_nome_completo()`. Por que? Porque esta claro que ele nao e um atributo da classe. Ele comeca com `get_` e a