Python Orientado a Objetos Parte 1

Introdução à Programação Orientada a Objetos

Imagine uma caixa de ferramentas. Cada ferramenta tem um propósito específico, assim como, no mundo da programação, cada objeto tem seu conjunto de características e comportamentos, agrupados de maneira a resolver uma parte de um problema maior. É aí que entra a Programação Orientada a Objetos (POO), um paradigma de programação baseado na ideia de “objetos” que contêm dados e métodos. A POO é essencial, pois facilita a organização do código, o torna mais reutilizável, compreensível e fácil de manter.

Construindo suas próprias classes em Python

Uma classe em Python é como um “plano” ou “receita” para criar objetos. Cada objeto criado a partir da classe é chamado de instância. Vejamos um exemplo simples:

No código acima, Carro é uma classe com dois atributos (modelo e ano) e um método (exibir_informacoes).

  1. A palavra-chave class é usada para definir uma classe chamada Carro. Em Python, é uma convenção nomear classes usando a notação PascalCase (primeira letra de cada palavra em maiúscula).
  2. Dentro da classe, o método __init__ é um método especial chamado de construtor. Ele é executado automaticamente quando um novo objeto da classe é criado. O construtor é usado para inicializar os atributos do objeto.
  3. O primeiro parâmetro do método __init__ é sempre self, que se refere à instância atual da classe. Além de self, o construtor recebe dois parâmetros: modelo e ano, que representam o modelo e o ano do carro, respectivamente.
  4. Dentro do construtor, self.modelo = modelo atribui o valor do parâmetro modelo ao atributo modelo do objeto atual (self). Da mesma forma, self.ano = ano atribui o valor do parâmetro ano ao atributo ano do objeto.
  5. O método exibir_informacoes é definido dentro da classe. Ele é usado para exibir as informações do carro.
  6. Dentro do método exibir_informacoes, print(f"Modelo: {self.modelo}, Ano: {self.ano}") imprime uma string formatada que inclui os valores dos atributos modelo e ano do objeto atual. A sintaxe f"..." é usada para criar uma f-string (string formatada) que permite a interpolação de variáveis dentro da string usando {}.

 

Agora, vamos ver como criar um objeto dessa classe e chamar o método exibir_informacoes:

 

Para criar um objeto dessa classe, você faria:

  1. A linha meu_carro = Carro("Fusca", 1967) cria um novo objeto da classe Carro e o atribui à variável meu_carro. Os argumentos "Fusca" e 1967 são passados para o construtor __init__ da classe, inicializando os atributos modelo e ano do objeto meu_carro.
  2. A linha meu_carro.exibir_informacoes() chama o método exibir_informacoes do objeto meu_carro. Isso imprime a string formatada contendo o modelo e o ano do carro.

Em resumo, a classe Carro define a estrutura e o comportamento de um carro, com atributos para o modelo e o ano, e um método para exibir essas informações. Ao criar um objeto da classe Carro, você pode especificar o modelo e o ano específicos do carro e chamar o método exibir_informacoes para ver essas informações.

 

Incrementando a Classe Carro

Vamos adicionar mais um método à classe Carro para ilustrar melhor o poder das classes. Neste exemplo, vamos adicionar um método para calcular o consumo de combustível do carro com base na distância percorrida e na quantidade de combustível consumida.

Neste exemplo, adicionamos os seguintes atributos e métodos à classe Carro:

  • Atributos:
    • odometro: representa a distância total percorrida pelo carro em quilômetros.
    • combustivel: representa a quantidade de combustível atualmente no tanque do carro em litros.
  • Métodos:
    • dirigir(distancia): simula o carro sendo dirigido por uma determinada distância em quilômetros. Ele atualiza o valor do odômetro e reduz a quantidade de combustível com base em um consumo médio assumido de 10 km/litro.
    • abastecer(quantidade): simula o abastecimento do carro com uma determinada quantidade de combustível em litros.
    • calcular_consumo(): calcula o consumo médio de combustível do carro com base na distância total percorrida e na quantidade de combustível consumida. Se o carro ainda não tiver sido dirigido, ele exibe uma mensagem informando isso.

Agora, vamos ver um exemplo de uso dessa classe atualizada:

 

Name Mangling em Python

O Python permite modificar o comportamento de acesso aos atributos utilizando uma técnica chamada Name Mangling. Isso é usado principalmente para criar variáveis “privadas” em uma classe. Por exemplo:

Neste caso, __saldo é um atributo “privado”. O Python altera internamente o nome do atributo para _ContaBancaria__saldo, que pode ser acessado somente dentro de métodos da própria classe.

 

Quando você define um atributo ou método com dois sublinhados à esquerda (__), o Python aplica automaticamente o Name Mangling. O nome do atributo ou método é modificado internamente, adicionando um sublinhado e o nome da classe como prefixo. Isso torna mais difícil acessar esses atributos ou métodos diretamente de fora da classe.

Vamos analisar o exemplo dado:

Neste exemplo, a classe ContaBancaria possui um atributo __saldo, que é considerado “privado”. Quando o Python encontra esse atributo, ele aplica o Name Mangling e modifica internamente o nome do atributo para _ContaBancaria__saldo.

Isso significa que, dentro da classe ContaBancaria, você pode acessar o atributo __saldo normalmente, como self.__saldo. No entanto, se você tentar acessar __saldo diretamente de fora da classe, receberá um erro de atributo não encontrado.

Por exemplo:

No entanto, é importante ressaltar que o Name Mangling não fornece uma privacidade absoluta. Ainda é possível acessar o atributo “privado” de fora da classe usando o nome modificado, ou seja, _ContaBancaria__saldo. Portanto, o Name Mangling é mais uma convenção para indicar que um atributo ou método deve ser tratado como privado e não deve ser acessado diretamente de fora da classe.

O Name Mangling também se aplica a métodos. Se você definir um método com dois sublinhados à esquerda, ele também será considerado “privado” e terá seu nome modificado internamente.

É importante observar que o Name Mangling só ocorre para atributos e métodos que começam com dois sublinhados (__) e não possuem mais de um sublinhado à direita. Atributos ou métodos com um único sublinhado à esquerda (_) são considerados “protegidos” por convenção, mas não têm seu nome modificado.

O Name Mangling é uma técnica útil para evitar conflitos de nomes e fornecer uma camada adicional de proteção para atributos e métodos que não devem ser acessados diretamente de fora da classe. No entanto, é importante lembrar que o Python não impõe uma privacidade estrita, e o Name Mangling é mais uma convenção do que uma medida de segurança absoluta.

Entendendo o ‘self'

O self é uma referência ao objeto atual e é usado para acessar variáveis que pertencem à classe. É passado automaticamente pelo Python quando você chama um método de um objeto.

Em Python, quando você define uma classe, o self é um parâmetro especial que é passado automaticamente para os métodos da classe. Ele representa a instância atual do objeto e é usado para acessar os atributos e métodos da classe dentro dos próprios métodos.

Vamos analisar o exemplo dado e expandir com mais detalhes:

Neste exemplo, a classe Pessoa possui três métodos: __init__, dizer_nome, dizer_idade e fazer_aniversario. Vamos analisar cada um deles:

  1. O método __init__ é o construtor da classe. Ele é chamado automaticamente quando um novo objeto é criado a partir da classe. O self é passado como primeiro parâmetro, seguido pelos outros parâmetros necessários para inicializar o objeto. Neste caso, nome e idade são passados como argumentos e atribuídos aos atributos self.nome e self.idade, respectivamente. Isso significa que cada instância da classe Pessoa terá seu próprio nome e idade.
  2. O método dizer_nome é usado para imprimir o nome da pessoa. Dentro do método, self.nome é usado para acessar o atributo nome do objeto atual.
  3. O método dizer_idade é usado para imprimir a idade da pessoa. Novamente, self.idade é usado para acessar o atributo idade do objeto atual.
  4. O método fazer_aniversario é usado para incrementar a idade da pessoa em 1 e imprimir uma mensagem de feliz aniversário. self.idade é incrementado usando self.idade += 1, atualizando a idade do objeto atual.

Agora, vamos criar algumas instâncias da classe Pessoa e chamar seus métodos:

Neste exemplo, criamos duas instâncias da classe Pessoa: pessoa1 e pessoa2. Cada instância tem seu próprio nome e idade. Quando chamamos os métodos dizer_nome e dizer_idade em cada instância, o self se refere à instância específica, acessando os atributos correspondentes.

Quando chamamos o método fazer_aniversario na pessoa1, a idade da pessoa1 é incrementada em 1 e a mensagem de feliz aniversário é impressa. Em seguida, ao chamar dizer_idade novamente na pessoa1, vemos que a idade foi atualizada para 26 anos.

O self permite que cada instância da classe tenha seus próprios atributos e estados independentes. Ele garante que os métodos da classe possam acessar e modificar os atributos específicos da instância em que estão sendo chamados.

Métodos de Classe (@classmethod) e Métodos Estáticos (@staticmethod)

Na Programação Orientada a Objetos em Python, além dos métodos de instância que agem sobre os atributos de um objeto específico, temos os métodos de classe e os métodos estáticos, que são decorados com @classmethod e @staticmethod, respectivamente. Esses métodos não operam em uma instância da classe, mas pertencem à classe.

Métodos de Classe (@classmethod)

Um método de classe recebe a classe como primeiro argumento, geralmente nomeado como cls. Eles podem acessar e modificar o estado da classe, e são comumente usados para criar instâncias da classe de formas alternativas.

Exemplo Prático de Método de Classe

Neste exemplo, numero_de_pessoas é um método de classe que retorna o valor do atributo de classe populacao.

Vamos explorar a utilização do método de classe que foi definido no exemplo anterior. O método de classe numero_de_pessoas é usado para rastrear quantas instâncias da classe Pessoa foram criadas. A seguir, demonstrarei como criar instâncias da classe Pessoa e como utilizar esse método de classe para acessar o número total de pessoas (ou instâncias).

Utilização do Método de Classe numero_de_pessoas

Vamos instanciar alguns objetos da classe Pessoa e ver como o método de classe pode ser utilizado para acessar a contagem de população:

No exemplo acima, cada vez que uma nova instância de Pessoa é criada, o construtor (__init__) incrementa o atributo de classe populacao. Para recuperar o valor atualizado da população, chamamos o método de classe numero_de_pessoas que está decorado com @classmethod.

Primeiro, criamos três instâncias da classe Pessoa, incrementando assim a população total para três. Em seguida, demonstramos duas formas de acessar o método de classe: diretamente através da classe Pessoa e, de maneira não tão comum, por meio de uma instância da classe.

Como podemos ver, os métodos de classe são úteis quando queremos executar uma operação que envolve a classe em si, e não objetos individuais daquela classe. Isso permite um gerenciamento mais fácil do estado global da classe, sem a necessidade de criar uma instância específica para acessá-lo.

Métodos Estáticos (@staticmethod)

Um método estático não recebe um argumento especial como self ou cls. Eles são como funções regulares que pertencem ao namespace da classe, e não podem acessar ou modificar o estado da classe. São úteis para organizar funções utilitárias que têm alguma conexão lógica com a classe.

Exemplo Prático de Método Estático

Aqui, somar é um método estático que simplesmente executa uma soma e retorna o resultado.

Ambos os tipos de métodos, de classe e estáticos, ajudam a manter nosso código organizado e claro, enquanto oferecem funcionalidades específicas relacionadas à classe, mas que não necessitam de uma instância da mesma. Ao usar @classmethod e @staticmethod, podemos garantir que métodos relacionados à lógica de uma classe estejam contidos dentro de seu namespace, contribuindo para a coesão do código.

Diferença Principal entre Métodos de Classe e Métodos Estáticos

A principal diferença entre um método de classe e um método estático reside na forma como eles se relacionam com a classe e suas instâncias. Embora ambos sejam associados à classe e não a uma instância específica, o método de classe está intrinsecamente ligado à própria classe, enquanto o método estático é mais independente.

Métodos de Classe (@classmethod)

  • Recebem a própria classe como primeiro argumento (convenção: cls).
  • Têm acesso ao estado da classe, o que significa que eles podem modificar atributos de classe e trabalhar com outras propriedades ou métodos de classe.
  • São usados para definir construtores alternativos, fornecendo diferentes maneiras de criar instâncias da classe.
  • Permitem uma herança mais flexível, onde subclasses podem fornecer diferentes implementações de um método de classe e podem chamar métodos de classe da classe pai com super().

Métodos Estáticos (@staticmethod)

  • Não recebem argumentos especiais como self ou cls. Eles se comportam como funções normais, mas estão dentro do namespace da classe.
  • Não têm acesso ao estado da classe e, por isso, não podem modificar atributos de classe ou acessar outros métodos da classe diretamente.
  • São usados para realizar funções utilitárias que são logicamente relacionadas à classe, mas não precisam de dados da classe ou de uma instância para operar.
  • Têm o mesmo comportamento em subclasses e não são afetados pela herança, o que significa que uma chamada a um método estático será sempre ao método definido na classe original, a menos que seja explicitamente sobrescrito na subclasse.

Exemplo Comparativo

Neste exemplo, metodo_de_classe tem acesso ao atributo valor da classe Exemplo e pode retornar ou modificar seu valor. Por outro lado, metodo_estatico não tem acesso ao estado da classe e trata-se mais de uma função regular agrupada logicamente com a classe Exemplo para conveniência e clareza.

Em resumo, escolher entre um método de classe e um método estático depende da necessidade de interagir com a estrutura da classe. Se a interação for necessária, um método de classe é a escolha certa. Caso contrário, para funções que operam de maneira independente, um método estático é o ideal.

Como Importar Classes em Python

Organizar seu código em módulos e classes é uma prática que traz clareza e reutilização ao seu trabalho. Vejamos como importar uma classe:

Suponha que você tenha uma classe Livro em um arquivo biblioteca.py:

Você pode importá-la em outro arquivo Python:

Conclusão

Neste artigo, viajamos pelos conceitos fundamentais da Programação Orientada a Objetos em Python. Nós construímos classes, lidamos com métodos estáticos e de classe, descobrimos o significado de self, desvendamos o mistério do Name Mangling e aprendemos como importar classes. Com estas ferramentas em mãos, você está bem equipado para estruturar seus programas de maneira eficiente e intuitiva. Lembre-se, a prática leva à perfeição, então experimente criar suas próprias classes e objetos para ver a POO em ação. Feliz programação!

Scroll to Top