Atributos e métodos

Veja como objetos guardam informações e executam ações, e como métodos podem colaborar entre si.

Você já sabe criar uma classe, definir atributos no __init__ e escrever métodos básicos. Agora vamos aprofundar: veja como atributos podem ter valores padrão, como métodos podem chamar outros métodos dentro da mesma classe, e como tudo isso se encaixa em um exemplo completo.

Atributos com valor padrão

No __init__, nem todo atributo precisa vir como parâmetro. Alguns podem começar com um valor padrão fixo, independente do que for passado ao criar o objeto:

class ContaBancaria:
   def __init__(self, titular, saldo_inicial):
       self.titular = titular
       self.saldo = saldo_inicial
       self.extrato = []   # começa vazio — não é passado como parâmetro
       self.ativa = True   # toda conta começa ativa por padrão

O self.extrato e o self.ativa são atributos que nascem com um valor já definido. Quem cria a conta não precisa (nem pode) passá-los — eles são inicializados automaticamente.

Atributos com parâmetros opcionais

Você também pode criar parâmetros com valor padrão no __init__, tornando-os opcionais na criação do objeto:

class Produto:
   def __init__(self, nome, preco, estoque=0):
       self.nome = nome
       self.preco = preco
       self.estoque = estoque  # se não informado, começa em 0

arroz = Produto("Arroz 5kg", 28.90, 150)  # estoque informado
cafe = Produto("Café 500g", 18.50)        # estoque não informado, começa em 0

print(arroz.estoque)  # 150
print(cafe.estoque)   # 0

Parâmetros com valor padrão funcionam em métodos da mesma forma que em funções comuns — eles devem sempre vir depois dos parâmetros obrigatórios.

Métodos que chamam outros métodos

Dentro de uma classe, um método pode chamar outro usando self.nome_do_metodo(). Isso é muito útil para evitar repetição e manter cada método com uma responsabilidade clara:

class ContaBancaria:
   def __init__(self, titular, saldo_inicial):
       self.titular = titular
       self.saldo = saldo_inicial
       self.extrato = []
       self.ativa = True

   def depositar(self, valor):
       self.saldo += valor
       self.extrato.append(f"+ R$ {valor:.2f}")  # sempre atualiza o extrato 

   def sacar(self, valor):
       if valor > self.saldo:
           print("Saldo insuficiente.")
           return
       self.saldo -= valor
       self.extrato.append(f"- R$ {valor:.2f}")  # sempre atualiza o extrato 

   def saldo_disponivel(self):
       return self.saldo

   def exibir_extrato(self):
       print(f"=== Extrato de {self.titular} ===")
       for movimento in self.extrato:
           print(movimento)
       print(f"Saldo atual: R$ {self.saldo_disponivel():.2f}")  # chama outro método!

No método exibir_extrato(), a chamada self.saldo_disponivel() acessa o resultado de outro método da mesma classe. Isso evita duplicar a lógica e deixa o código mais organizado.

Se chegar um dia em que precisamos mudar como o saldo disponível é calculado — por exemplo, descontando uma taxa — basta alterar saldo_disponivel() e exibir_extrato() continuará funcionando sem mudança.

conta = ContaBancaria("Ana Silva", 1000.00)
conta.depositar(500.00)
conta.sacar(200.00)
conta.sacar(2000.00)  # Saldo insuficiente.
conta.exibir_extrato()

Saída:

Saldo insuficiente.

=== Extrato de Ana Silva ===
+ R$ 500.00
- R$ 200.00
Saldo atual: R$ 1300.00

Modificando atributos por fora da classe

Além dos métodos, você pode alterar um atributo diretamente de fora da classe:

conta = ContaBancaria("Bruno Costa", 500.00)
print(conta.titular)  # Bruno Costa

conta.titular = "Bruno Costa Jr."  # modificação direta
print(conta.titular)  # Bruno Costa Jr.

Exemplo prático: sistema de alunos

Vamos construir uma classe completa que combina tudo que aprendemos: atributos padrão, parâmetros opcionais, métodos chamando outros métodos e múltiplos objetos:

class Aluno:
   def __init__(self, nome, matricula, curso="Não informado"):
       self.nome = nome
       self.matricula = matricula
       self.curso = curso
       self.notas = []  # atributo padrão — começa sempre vazio

   def adicionar_nota(self, nota):
       if nota < 0 or nota > 10:
           print(f"Nota inválida: {nota}. Use valores entre 0 e 10.")
           return
       self.notas.append(nota)

   def calcular_media(self):
       if not self.notas:
           return 0
       soma = sum(self.notas)
       quantidade = len(self.notas)
       media = soma / quantidade
       return media

   def situacao(self):
       media = self.calcular_media()  # chama outro método
       if media >= 7:
           return "Aprovado"
       elif media >= 5:
           return "Recuperação"
       else:
           return "Reprovado"

   def relatorio(self):
       media = self.calcular_media()    # chama outro método
       status = self.situacao()         # chama outro método
       print(f"\n=== {self.nome} ===")
       print(f"Matrícula : {self.matricula}")
       print(f"Curso     : {self.curso}")
       print(f"Notas     : {self.notas}")
       print(f"Média     : {media:.1f}")
       print(f"Situação  : {status}")

Veja como relatorio() chama calcular_media() e situacao(), e como situacao() chama calcular_media(). Cada método faz uma coisa só, e eles colaboram entre si:

# Criando objetos
ana = Aluno("Ana Silva", "2026001", "Desenvolvimento Web")
bruno = Aluno("Bruno Costa", "2026002")  # curso usa o valor padrão

# Adicionando notas
ana.adicionar_nota(8.5)
ana.adicionar_nota(7.0)
ana.adicionar_nota(9.0)
ana.adicionar_nota(11)   # Nota inválida: 11. Use valores entre 0 e 10.

bruno.adicionar_nota(4.0)
bruno.adicionar_nota(5.5)

# Exibindo relatórios
ana.relatorio()
bruno.relatorio()

Saída:

Nota inválida: 11. Use valores entre 0 e 10.

=== Ana Silva ===
Matrícula : 2026001
Curso     : Desenvolvimento Web
Notas     : [8.5, 7.0, 9.0]
Média     : 8.2
Situação  : Aprovado

=== Bruno Costa ===
Matrícula : 2026002
Curso     : Não informado
Notas     : [4.0, 5.5]
Média     : 4.8
Situação  : Reprovado

Repare que ana.notas e bruno.notas são listas completamente independentes — cada objeto tem sua própria cópia dos atributos, mesmo quando eles nascem com o mesmo valor padrão.

O que aprendemos nesta lição

Nesta lição, você aprendeu que:

  • Atributos podem ter valores padrão fixos no __init__ (como self.notas = []), sem precisar ser passados como parâmetro.
  • Parâmetros do __init__ podem ser opcionais com valor padrão (como curso="Não informado"), exatamente como em funções comuns.
  • Métodos podem chamar outros métodos da mesma classe usando self.nome_do_metodo(), evitando repetição e mantendo responsabilidades separadas.
  • Atributos podem ser modificados diretamente de fora da classe com objeto.atributo = novo_valor.
  • Cada objeto tem sua própria cópia independente dos atributos — alterar ana.notas não afeta bruno.notas.