Python

Classes e Objetos: os fundamentos da Orientação a Objetos Já leu

7 min de leitura

Classes e Objetos: os fundamentos da Orientação a Objetos
Até aqui trabalhamos com funções e estruturas de dados separadas. A Orientação a Objetos (OO) propõe uma forma diferen

Até aqui trabalhamos com funções e estruturas de dados separadas. A Orientação a Objetos (OO) propõe uma forma diferente de organizar o código: agrupando dados e comportamentos relacionados em uma única unidade chamada objeto. É o paradigma dominante no desenvolvimento de software moderno e Python o suporta de forma elegante e completa.


O que é uma Classe?

Uma classe é um molde — uma definição de como um objeto deve ser estruturado. O objeto é uma instância concreta criada a partir desse molde.

A analogia clássica: a planta de uma casa é a classe; cada casa construída a partir dela é um objeto.

class Cachorro:
    """Representa um cachorro."""

    def __init__(self, nome, raca, idade):
        self.nome  = nome
        self.raca  = raca
        self.idade = idade

    def latir(self):
        return f"{self.nome} diz: Au au!"

    def apresentar(self):
        return f"{self.nome} é um {self.raca} de {self.idade} ano(s)."


# Criando objetos (instâncias)
rex   = Cachorro("Rex",   "Pastor Alemão", 3)
bolinha = Cachorro("Bolinha", "Poodle",    1)

print(rex.latir())         # Rex diz: Au au!
print(bolinha.apresentar()) # Bolinha é um Poodle de 1 ano(s).
print(rex.nome)             # Rex

O Método init

O método __init__ é o construtor da classe — executado automaticamente quando um objeto é criado. Ele inicializa os atributos do objeto.

O parâmetro self representa o próprio objeto sendo criado. É sempre o primeiro parâmetro de qualquer método de instância — Python o passa automaticamente.

class Produto:
    def __init__(self, nome, preco, estoque=0):
        self.nome    = nome
        self.preco   = preco
        self.estoque = estoque

    def disponivel(self):
        return self.estoque > 0

    def aplicar_desconto(self, percentual):
        self.preco *= (1 - percentual / 100)

    def __repr__(self):
        return f"Produto('{self.nome}', R${self.preco:.2f}, estoque={self.estoque})"


teclado = Produto("Teclado Mecânico", 450.00, 15)
mouse   = Produto("Mouse Gamer",      189.90)

print(teclado.disponivel())   # True
print(mouse.disponivel())     # False

teclado.aplicar_desconto(10)
print(teclado)  # Produto('Teclado Mecânico', R$405.00, estoque=15)

Atributos de Instância vs. Atributos de Classe

Atributos de instância pertencem a cada objeto individualmente. Atributos de classe são compartilhados por todas as instâncias:

class Funcionario:
    empresa = "TechCorp"      # atributo de classe — compartilhado
    _contador = 0             # conta quantos funcionários foram criados

    def __init__(self, nome, salario):
        self.nome    = nome       # atributo de instância
        self.salario = salario
        Funcionario._contador += 1

    @classmethod
    def total_funcionarios(cls):
        return cls._contador

    @staticmethod
    def categoria_salarial(salario):
        if salario < 3000:
            return "Júnior"
        elif salario < 7000:
            return "Pleno"
        return "Sênior"


f1 = Funcionario("Ana",    4500)
f2 = Funcionario("Bruno",  8200)
f3 = Funcionario("Carla",  2800)

print(Funcionario.empresa)                    # TechCorp
print(Funcionario.total_funcionarios())       # 3
print(Funcionario.categoria_salarial(4500))   # Pleno
print(f1.empresa)                             # TechCorp — acessível via instância

Métodos Especiais (Dunder Methods)

Python usa métodos com duplo underscore para integrar objetos ao comportamento nativo da linguagem:

class Vetor:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        """Representação técnica — usada no console."""
        return f"Vetor({self.x}, {self.y})"

    def __str__(self):
        """Representação legível — usada por print()."""
        return f"({self.x}, {self.y})"

    def __add__(self, outro):
        """Permite usar o operador +."""
        return Vetor(self.x + outro.x, self.y + outro.y)

    def __mul__(self, escalar):
        """Permite multiplicar por um número."""
        return Vetor(self.x * escalar, self.y * escalar)

    def __eq__(self, outro):
        """Permite comparar com ==."""
        return self.x == outro.x and self.y == outro.y

    def __len__(self):
        """Permite usar len()."""
        return 2

    def magnitude(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5


v1 = Vetor(3, 4)
v2 = Vetor(1, 2)

print(v1)            # (3, 4)
print(repr(v1))      # Vetor(3, 4)
print(v1 + v2)       # (4, 6)
print(v1 * 3)        # (9, 12)
print(v1 == Vetor(3, 4))  # True
print(v1.magnitude())     # 5.0

Encapsulamento

Encapsulamento é o princípio de proteger os dados internos de um objeto, expondo apenas o que é necessário. Python usa convenções de nomenclatura:

class ContaBancaria:
    def __init__(self, titular, saldo_inicial=0):
        self.titular   = titular
        self._saldo    = saldo_inicial    # _ indica "protegido" — convenção
        self.__historico = []             # __ indica "privado" — name mangling

    @property
    def saldo(self):
        """Propriedade — acesso de leitura ao saldo."""
        return self._saldo

    def depositar(self, valor):
        if valor <= 0:
            raise ValueError("Valor de depósito deve ser positivo.")
        self._saldo += valor
        self.__historico.append(f"Depósito: +R${valor:.2f}")

    def sacar(self, valor):
        if valor <= 0:
            raise ValueError("Valor de saque deve ser positivo.")
        if valor > self._saldo:
            raise ValueError("Saldo insuficiente.")
        self._saldo -= valor
        self.__historico.append(f"Saque: -R${valor:.2f}")

    def extrato(self):
        print(f"Titular: {self.titular}")
        print(f"Saldo:   R${self._saldo:.2f}")
        print("Histórico:")
        for item in self.__historico:
            print(f"  {item}")


conta = ContaBancaria("Ricardo", 1000)
conta.depositar(500)
conta.sacar(200)
conta.extrato()

print(conta.saldo)    # 1300.0 — acesso via property
# conta._saldo = 999999  # possível, mas viola a convenção
# conta.__historico      # AttributeError — name mangling protege

Exemplo Completo: Sistema de Biblioteca

class Livro:
    def __init__(self, titulo, autor, isbn):
        self.titulo    = titulo
        self.autor     = autor
        self.isbn      = isbn
        self._emprestado = False

    @property
    def disponivel(self):
        return not self._emprestado

    def __repr__(self):
        status = "disponível" if self.disponivel else "emprestado"
        return f"'{self.titulo}' por {self.autor} [{status}]"


class Biblioteca:
    def __init__(self, nome):
        self.nome   = nome
        self._acervo = {}

    def adicionar(self, livro):
        self._acervo[livro.isbn] = livro
        print(f"Livro adicionado: {livro.titulo}")

    def emprestar(self, isbn, usuario):
        livro = self._acervo.get(isbn)
        if not livro:
            print("Livro não encontrado.")
            return
        if not livro.disponivel:
            print(f"'{livro.titulo}' já está emprestado.")
            return
        livro._emprestado = True
        print(f"'{livro.titulo}' emprestado para {usuario}.")

    def devolver(self, isbn):
        livro = self._acervo.get(isbn)
        if livro and not livro.disponivel:
            livro._emprestado = False
            print(f"'{livro.titulo}' devolvido com sucesso.")

    def listar(self):
        print(f"\nAcervo — {self.nome}")
        print("-" * 40)
        for livro in self._acervo.values():
            print(f"  {livro}")


biblioteca = Biblioteca("Biblioteca Central")

l1 = Livro("Python Fluente",       "Luciano Ramalho",  "978-1492056355")
l2 = Livro("Python Crash Course",  "Eric Matthes",     "978-1593279288")
l3 = Livro("Grokking Algorithms",  "Aditya Bhargava",  "978-1617292231")

biblioteca.adicionar(l1)
biblioteca.adicionar(l2)
biblioteca.adicionar(l3)

biblioteca.emprestar("978-1492056355", "Ana")
biblioteca.emprestar("978-1492056355", "Bruno")  # já emprestado
biblioteca.listar()
biblioteca.devolver("978-1492056355")
biblioteca.listar()

Resumo

  • Classes são moldes; objetos são instâncias concretas criadas a partir delas
  • __init__ inicializa os atributos do objeto; self referencia a instância atual
  • Atributos de classe são compartilhados; atributos de instância são individuais
  • @classmethod recebe a classe como primeiro argumento; @staticmethod não recebe nenhum
  • Métodos dunder integram objetos ao comportamento nativo do Python (+, ==, str(), len())
  • Encapsulamento usa _ para protegido e __ para privado por convenção
  • @property transforma um método em atributo de leitura, controlando o acesso

Referências e Leituras Complementares

Comentários

Mais em Python

Decoradores e Metaprogramação
Decoradores e Metaprogramação

Decoradores s&atilde;o um dos recursos mais elegantes do Python. Eles permite...

Dicionários: chave, valor e as estruturas do mundo real
Dicionários: chave, valor e as estruturas do mundo real

Se listas organizam dados por posi&ccedil;&atilde;o, dicion&aacute;rios organ...

Laços de Repetição: for e while
Laços de Repetição: for e while

Computadores s&atilde;o excepcionalmente bons em repetir tarefas &mdash; muit...