Classes e objetos em Python
LidoNesta aula, você aprenderá os fundamentos da programação orientada a objetos em Python: como definir classes, criar objetos, usar o construtor __init__, o parâmetro self, e trabalhar com atributos e métodos. Ao final, você será capaz de modelar entidades do mundo real em código Python de forma clara e reutilizável.
Bem-vindo à aula sobre Classes e Objetos em Python! Até agora, você provavelmente já escreveu scripts que usam funções e estruturas de dados, mas e se precisar representar algo mais complexo, como um usuário de um sistema, um carro ou uma conta bancária? A programação orientada a objetos (POO) oferece uma maneira poderosa de organizar código, agrupando dados e comportamentos relacionados em entidades chamadas classes e objetos. Nesta aula, vamos explorar os conceitos essenciais de POO em Python, desde a definição de uma classe até a criação e uso de objetos.
Python é uma linguagem que suporta múltiplos paradigmas, e a POO é um dos mais importantes, especialmente para projetos grandes e colaborativos. Ao dominar classes e objetos, você poderá escrever código mais modular, reutilizável e fácil de manter. Vamos começar!
class
Uma classe é um modelo (ou blueprint) para criar objetos. Ela define quais atributos (dados) e métodos (funções) os objetos criados a partir dela terão. Pense em uma classe como uma forma de bolo: a forma define o formato, mas cada bolo feito com ela pode ter coberturas diferentes. Em Python, definimos uma classe usando a palavra-chave class, seguida do nome da classe (por convenção, em CamelCase).
Sintaxe básica:
class NomeDaClasse:
# corpo da classe
passO corpo da classe pode conter atributos (variáveis de classe) e métodos (funções). Vejamos um exemplo simples:
class Pessoa:
# atributo de classe (compartilhado por todas as instâncias)
especie = "Humano"
# método
def saudacao(self):
return "Olá!"Neste exemplo, definimos a classe Pessoa com um atributo de classe especie e um método saudacao. Note que o método recebe o parâmetro self (mais sobre isso em breve). Por enquanto, não criamos nenhum objeto ainda; apenas definimos a estrutura.
__init__ e self
O método __init__ é um método especial chamado construtor. Ele é executado automaticamente quando criamos um novo objeto (instância) da classe. Serve para inicializar os atributos do objeto com valores específicos. O nome __init__ é fixo e sempre deve ter self como primeiro parâmetro.
O parâmetro self refere-se à própria instância atual. Dentro dos métodos, usamos self para acessar os atributos e outros métodos do objeto. Quando chamamos um método de um objeto, o Python passa automaticamente o objeto como o primeiro argumento, que é atribuído a self.
Exemplo de uso do construtor:
class Pessoa:
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
def apresentar(self):
return f"Meu nome é {self.nome} e tenho {self.idade} anos."Ao criar um objeto, passamos os argumentos que o __init__ espera (exceto self):
p1 = Pessoa("Ana", 25)
print(p1.nome) # Ana
print(p1.apresentar()) # Meu nome é Ana e tenho 25 anos.Observe que não chamamos __init__ explicitamente; ele é invocado automaticamente quando usamos Pessoa("Ana", 25).
Atributos e métodos
Atributos são variáveis que pertencem a um objeto (atributos de instância) ou à classe (atributos de classe). Já os métodos são funções definidas dentro da classe que descrevem os comportamentos dos objetos. Vamos detalhar:
- Atributos de instância: definidos dentro de métodos, geralmente no
__init__, precedidos porself.. Cada objeto tem seus próprios valores. - Atributos de classe: definidos diretamente no corpo da classe, fora de métodos. São compartilhados por todas as instâncias.
- Métodos de instância: funções que recebem
selfcomo primeiro parâmetro e podem acessar/modificar atributos do objeto.
Exemplo completo:
class ContaBancaria:
# atributo de classe
banco = "Banco do Brasil"
def __init__(self, titular, saldo=0):
# atributos de instância
self.titular = titular
self.saldo = saldo
def depositar(self, valor):
if valor > 0:
self.saldo += valor
return f"Depósito de R${valor} realizado. Novo saldo: R${self.saldo}"
return "Valor inválido"
def sacar(self, valor):
if 0 < valor <= self.saldo:
self.saldo -= valor
return f"Saque de R${valor} realizado. Novo saldo: R${self.saldo}"
return "Saldo insuficiente ou valor inválido"
def exibir_saldo(self):
return f"Saldo atual: R${self.saldo}"Testando:
conta = ContaBancaria("João", 500)
print(conta.banco) # Banco do Brasil
print(conta.exibir_saldo()) # Saldo atual: R$500
conta.depositar(150)
print(conta.exibir_saldo()) # Saldo atual: R$650
conta.sacar(200)
print(conta.exibir_saldo()) # Saldo atual: R$450Note que banco é um atributo de classe e pode ser acessado tanto via instância (conta.banco) quanto via classe (ContaBancaria.banco).
Instanciando
Instanciar é o processo de criar um objeto a partir de uma classe. Para isso, chamamos a classe como se fosse uma função, passando os argumentos necessários para o __init__. O resultado é uma instância (objeto) que possui seus próprios atributos e métodos.
Vejamos mais exemplos:
class Carro:
def __init__(self, marca, modelo, ano):
self.marca = marca
self.modelo = modelo
self.ano = ano
self.velocidade = 0
def acelerar(self, incremento):
self.velocidade += incremento
return f"Velocidade atual: {self.velocidade} km/h"
def frear(self, decremento):
self.velocidade = max(0, self.velocidade - decremento)
return f"Velocidade atual: {self.velocidade} km/h"
# Criando objetos (instâncias)
carro1 = Carro("Toyota", "Corolla", 2020)
carro2 = Carro("Honda", "Civic", 2022)
print(carro1.marca) # Toyota
print(carro2.marca) # Honda
print(carro1.acelerar(30)) # Velocidade atual: 30 km/h
print(carro2.acelerar(50)) # Velocidade atual: 50 km/hCada objeto criado é independente: alterar atributos de um não afeta o outro. Isso é fundamental na POO.
Podemos também verificar o tipo de um objeto e acessar seus atributos dinamicamente:
print(type(carro1)) # <class '__main__.Carro'>
print(isinstance(carro1, Carro)) # True
Agora que você entende como criar e usar classes, vamos explorar boas práticas e exercícios para fixar o conteúdo.
Boas práticas
- Use nomes de classes em CamelCase (ex.:
MinhaClasse). - Sempre inclua o método
__init__para inicializar atributos, mesmo que seja vazio. - Utilize
selfpara acessar atributos e métodos dentro da classe. - Mantenha a coesão: cada classe deve ter uma responsabilidade única.
- Prefira atributos privados (com dois underscores) quando não quiser acesso externo direto, mas lembre-se de que em Python isso é uma convenção, não uma restrição rígida.
- Documente suas classes e métodos com docstrings.
Exercícios
- Crie uma classe
Retangulocom atributoslarguraealtura(float), um métodoarea()que retorna a área, e um métodoperimetro()que retorna o perímetro. Instancie um retângulo de 5x3 e imprima área e perímetro.✓ Resposta:class Retangulo: def __init__(self, largura, altura): self.largura = largura self.altura = altura def area(self): return self.largura * self.altura def perimetro(self): return 2 * (self.largura + self.altura) ret = Retangulo(5, 3) print(f"Área: {ret.area()}") # 15 print(f"Perímetro: {ret.perimetro()}") # 16 - Defina uma classe
Livrocom título, autor e número de páginas. Adicione um métododescricao()que retorna uma string formatada. Crie dois livros e imprima as descrições.✓ Resposta:class Livro: def __init__(self, titulo, autor, paginas): self.titulo = titulo self.autor = autor self.paginas = paginas def descricao(self): return f"'{self.titulo}' por {self.autor} - {self.paginas} páginas" livro1 = Livro("Dom Casmurro", "Machado de Assis", 256) livro2 = Livro("1984", "George Orwell", 328) print(livro1.descricao()) print(livro2.descricao()) - Crie uma classe
Pessoacom nome, idade e um métodofazer_aniversario()que incrementa a idade em 1. Instancie uma pessoa, chame o método e mostre a nova idade.✓ Resposta:class Pessoa: def __init__(self, nome, idade): self.nome = nome self.idade = idade def fazer_aniversario(self): self.idade += 1 return f"Parabéns, {self.nome}! Agora você tem {self.idade} anos." p = Pessoa("Maria", 30) print(p.fazer_aniversario()) # Parabéns, Maria! Agora você tem 31 anos. - Crie uma classe
ContaBancariacom titular, saldo inicial (0 por padrão), métodosdepositar(valor)esacar(valor). Adicione uma validação para não deixar saldo negativo. Teste com depósitos e saques.✓ Resposta:class ContaBancaria: def __init__(self, titular, saldo=0): self.titular = titular self.saldo = saldo def depositar(self, valor): if valor > 0: self.saldo += valor return f"Depósito de R${valor:.2f} realizado. Saldo: R${self.saldo:.2f}" return "Valor de depósito inválido" def sacar(self, valor): if 0 < valor <= self.saldo: self.saldo -= valor return f"Saque de R${valor:.2f} realizado. Saldo: R${self.saldo:.2f}" return "Saldo insuficiente ou valor inválido" conta = ContaBancaria("Carlos", 1000) print(conta.depositar(200)) # Depósito de R$200.00 realizado. Saldo: R$1200.00 print(conta.sacar(1500)) # Saldo insuficiente ou valor inválido print(conta.sacar(500)) # Saque de R$500.00 realizado. Saldo: R$700.00 - Crie uma classe
Alunocom nome, matrícula e uma lista de notas. Adicione um métodomedia()que retorna a média das notas e um métodosituacao()que retorna "Aprovado" se média ≥ 7, "Recuperação" se média ≥ 4 e < 7, e "Reprovado" se média < 4. Instancie um aluno com notas e imprima a situação.✓ Resposta:class Aluno: def __init__(self, nome, matricula, notas): self.nome = nome self.matricula = matricula self.notas = notas def media(self): if not self.notas: return 0 return sum(self.notas) / len(self.notas) def situacao(self): media = self.media() if media >= 7: return "Aprovado" elif media >= 4: return "Recuperação" else: return "Reprovado" aluno = Aluno("Beatriz", "2021001", [8, 7.5, 9]) print(f"Média: {aluno.media():.2f}") print(f"Situação: {aluno.situacao()}")