Fundamentos do Python para Iniciantes

0/31 aulas0%
pratica

Atributos e Métodos: Criando Sua Primeira Classe

Aprenda sobre atributos e métodos: criando sua primeira classe

50 min
Aula 5 de 6

Atributos e Métodos: Criando Sua Primeira Classe 🐍

Olá, futuros mestres do Python! 👋 Sejam bem-vindos a mais uma aula do nosso curso de Fundamentos do Python. Hoje, vamos dar um salto gigantesco no mundo da Programação Orientada a Objetos (POO) ao aprender sobre classes, atributos e métodos.

Esta aula é prática! Vamos colocar a mão na massa para construir nossas primeiras classes e entender como elas moldam o comportamento dos nossos programas. Prepare-se para criar seus próprios "tipos" de dados!


1. Introdução: O Que São Classes, Atributos e Métodos? 💡

Na Programação Orientada a Objetos, pensamos em termos de "objetos" que interagem entre si. Mas, o que é um objeto? Um objeto é uma instância de uma classe.

  • Classe: Pense em uma classe como um molde ou um projeto (blueprint) para criar objetos. Ela define as características (atributos) e os comportamentos (métodos) que todos os objetos criados a partir dela terão. Por exemplo, a classe Carro define que todo carro tem cor, modelo e pode acelerar ou frear.
  • Atributos: São as características ou os dados associados a uma classe ou a uma instância de uma classe. No exemplo do Carro, cor e modelo seriam atributos.
  • Métodos: São as ações ou os comportamentos que um objeto pode realizar. No exemplo do Carro, acelerar e frear seriam métodos.

Vamos começar a criar! 🚀


2. Definindo Sua Primeira Classe em Python 🏗️

Em Python, definimos uma classe usando a palavra-chave class, seguida do nome da classe (por convenção, nomes de classes começam com letra maiúscula).

2.1. A Classe Pessoa

Vamos criar uma classe simples chamada Pessoa.

# Exemplo 1: Definindo uma classe simples
class Pessoa:
    pass # 'pass' é uma instrução nula, usada quando você não quer adicionar nada ainda.
 
# Criando um objeto (instância) da classe Pessoa
pessoa1 = Pessoa()
pessoa2 = Pessoa()
 
print(type(pessoa1))
print(type(pessoa2))
print(pessoa1)
print(pessoa2)

Saída esperada:

<class '__main__.Pessoa'>
<class '__main__.Pessoa'>
<__main__.Pessoa object at 0x...>
<__main__.Pessoa object at 0x...>

Perceba que pessoa1 e pessoa2 são objetos distintos da mesma classe Pessoa.


3. Atributos: As Características dos Nossos Objetos 🏷️

Existem dois tipos principais de atributos:

  1. Atributos de Instância: Pertencem a uma instância específica da classe. Cada objeto tem sua própria cópia desses atributos.
  2. Atributos de Classe: Pertencem à classe em si e são compartilhados por todas as instâncias daquela classe.

3.1. Atributos de Instância e o Método __init__ 🛠️

O método especial __init__ (lê-se "init", de initialize) é o construtor da classe. Ele é chamado automaticamente toda vez que você cria uma nova instância da classe. É o local ideal para definir os atributos de instância.

  • self: O primeiro parâmetro de qualquer método de instância (incluindo __init__) é sempre self. Ele representa a própria instância do objeto. É através de self que acessamos os atributos e métodos daquela instância.
# Exemplo 2: Classe Pessoa com atributos de instância
class Pessoa:
    def __init__(self, nome, idade):
        """
        Método construtor da classe Pessoa.
        Inicializa uma nova instância de Pessoa com um nome e uma idade.
        """
        self.nome = nome  # Atributo de instância
        self.idade = idade # Atributo de instância
 
# Criando instâncias da classe Pessoa
pessoa_a = Pessoa("Alice", 30)
pessoa_b = Pessoa("Bob", 25)
 
print(f"Pessoa A: Nome = {pessoa_a.nome}, Idade = {pessoa_a.idade}")
print(f"Pessoa B: Nome = {pessoa_b.nome}, Idade = {pessoa_b.idade}")
 
# Podemos até modificar atributos de uma instância específica
pessoa_a.idade = 31
print(f"Pessoa A (após modificação): Nome = {pessoa_a.nome}, Idade = {pessoa_a.idade}")

Saída esperada:

Pessoa A: Nome = Alice, Idade = 30
Pessoa B: Nome = Bob, Idade = 25
Pessoa A (após modificação): Nome = Alice, Idade = 31

3.2. Atributos de Classe 🌍

Atributos de classe são definidos diretamente dentro do corpo da classe, fora de qualquer método.

# Exemplo 3: Classe com atributo de classe
class Carro:
    # Atributo de classe: compartilhado por todas as instâncias de Carro
    rodas = 4
    tipo_combustivel = "Gasolina"
 
    def __init__(self, marca, modelo, cor):
        self.marca = marca    # Atributo de instância
        self.modelo = modelo  # Atributo de instância
        self.cor = cor        # Atributo de instância
 
# Criando instâncias de Carro
carro1 = Carro("Ford", "Focus", "Prata")
carro2 = Carro("Chevrolet", "Onix", "Preto")
 
print(f"Carro 1: Marca={carro1.marca}, Rodas={carro1.rodas}, Combustível={carro1.tipo_combustivel}")
print(f"Carro 2: Marca={carro2.marca}, Rodas={carro2.rodas}, Combustível={carro2.tipo_combustivel}")
 
# Acessando o atributo de classe diretamente pela classe
print(f"Todos os carros têm {Carro.rodas} rodas.")
 
# Modificando um atributo de classe afeta todas as instâncias (se acessado via classe)
Carro.rodas = 6 # Imagine um carro futurista!
print(f"Carro 1 (após mudança): Rodas={carro1.rodas}")
print(f"Carro 2 (após mudança): Rodas={carro2.rodas}")

Saída esperada:

Carro 1: Marca=Ford, Rodas=4, Combustível=Gasolina
Carro 2: Marca=Chevrolet, Rodas=4, Combustível=Gasolina
Todos os carros têm 4 rodas.
Carro 1 (após mudança): Rodas=6
Carro 2 (após mudança): Rodas=6

4. Métodos: Os Comportamentos dos Nossos Objetos ⚙️

Métodos são funções definidas dentro de uma classe. Eles podem operar nos atributos da instância ou da classe.

4.1. Métodos de Instância 🏃‍♀️

São os mais comuns. Eles operam em uma instância específica da classe e sempre recebem self como primeiro argumento.

# Exemplo 4: Classe Pessoa com método de instância
class Pessoa:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
 
    def apresentar(self):
        """
        Método de instância para apresentar a pessoa.
        Acessa os atributos de instância 'nome' e 'idade'.
        """
        return f"Olá, meu nome é {self.nome} e eu tenho {self.idade} anos."
 
    def aniversario(self):
        """
        Método de instância para aumentar a idade da pessoa.
        Modifica o atributo de instância 'idade'.
        """
        self.idade += 1
        print(f"Feliz aniversário, {self.nome}! Agora você tem {self.idade} anos.")
 
# Criando instâncias
pessoa1 = Pessoa("Carlos", 28)
pessoa2 = Pessoa("Diana", 22)
 
# Chamando métodos de instância
print(pessoa1.apresentar())
print(pessoa2.apresentar())
 
pessoa1.aniversario()
print(pessoa1.apresentar())

Saída esperada:

Olá, meu nome é Carlos e eu tenho 28 anos.
Olá, meu nome é Diana e eu tenho 22 anos.
Feliz aniversário, Carlos! Agora você tem 29 anos.
Olá, meu nome é Carlos e eu tenho 29 anos.

4.2. Métodos de Classe (@classmethod) 🏭

Métodos de classe operam na classe em si, não em uma instância específica. Eles são decorados com @classmethod e recebem cls (convenção para "class") como primeiro argumento, em vez de self. São úteis para criar métodos de fábrica (alternative constructors) ou para operar em atributos de classe.

# Exemplo 5: Classe com método de classe
class Livro:
    tipo_publicacao = "Livro Impresso" # Atributo de classe
 
    def __init__(self, titulo, autor, ano):
        self.titulo = titulo
        self.autor = autor
        self.ano = ano
 
    def detalhes(self):
        return f"Título: {self.titulo}, Autor: {self.autor}, Ano: {self.ano}, Tipo: {self.tipo_publicacao}"
 
    @classmethod
    def criar_livro_digital(cls, titulo, autor, ano):
        """
        Método de classe para criar um livro digital.
        Altera temporariamente o tipo de publicação para "Livro Digital".
        """
        # Podemos acessar atributos de classe via cls
        original_tipo = cls.tipo_publicacao
        cls.tipo_publicacao = "Livro Digital"
        livro_digital = cls(titulo, autor, ano) # Cria uma nova instância
        cls.tipo_publicacao = original_tipo # Restaura o atributo de classe
        return livro_digital
 
# Criando um livro normal
livro1 = Livro("Dom Casmurro", "Machado de Assis", 1899)
print(livro1.detalhes())
 
# Usando o método de classe para criar um livro digital
livro_digital = Livro.criar_livro_digital("Python Fluente", "Luciano Ramalho", 2015)
print(livro_digital.detalhes())
 
# Verificando se o tipo_publicacao do Livro original não foi alterado globalmente
# Note: o comportamento acima de alterar cls.tipo_publicacao é um exemplo didático.
# Em cenários reais, para um livro digital, você provavelmente teria um atributo de instância
# 'formato' ou uma subclasse 'LivroDigital'.
print(f"Tipo de publicação padrão da classe Livro após a criação do digital: {Livro.tipo_publicacao}")

Saída esperada:

Título: Dom Casmurro, Autor: Machado de Assis, Ano: 1899, Tipo: Livro Impresso
Título: Python Fluente, Autor: Luciano Ramalho, Ano: 2015, Tipo: Livro Digital
Tipo de publicação padrão da classe Livro após a criação do digital: Livro Impresso

4.3. Métodos Estáticos (@staticmethod) 🔗

Métodos estáticos são funções que pertencem à classe, mas não operam nem na instância (self) nem na classe (cls). Eles são como funções regulares, mas estão logicamente agrupados dentro de uma classe porque de alguma forma se relacionam a ela. São decorados com @staticmethod.

# Exemplo 6: Classe com método estático
class Calculadora:
    def __init__(self, valor_inicial=0):
        self.valor = valor_inicial
 
    def adicionar(self, num):
        self.valor += num
        return self.valor
 
    @staticmethod
    def somar(a, b):
        """
        Método estático para somar dois números.
        Não precisa de acesso a 'self' ou 'cls'.
        """
        return a + b
 
    @staticmethod
    def multiplicar(a, b):
        """
        Método estático para multiplicar dois números.
        """
        return a * b
 
# Usando o método de instância
calc = Calculadora(10)
print(f"Valor atual da calculadora: {calc.adicionar(5)}")
 
# Usando métodos estáticos (podem ser chamados via classe ou instância)
print(f"Soma de 7 e 3: {Calculadora.somar(7, 3)}")
print(f"Multiplicação de 4 e 5: {Calculadora.multiplicar(4, 5)}")
 
# Também podemos chamar via instância, mas não é o propósito principal
print(f"Soma de 10 e 20 (via instância): {calc.somar(10, 20)}")

Saída esperada:

Valor atual da calculadora: 15
Soma de 7 e 3: 10
Multiplicação de 4 e 5: 20
Soma de 10 e 20 (via instância): 30

5. Exercícios/Desafios Práticos 🧑‍💻

Agora é sua vez de aplicar o que aprendemos! Crie as seguintes classes, atributos e métodos.

Desafio 1: Classe Livro 📚

Crie uma classe Livro com os seguintes requisitos:

  • Atributos de Instância:
    • titulo (string)
    • autor (string)
    • paginas (inteiro)
    • emprestado (booleano, padrão False)
  • Método de Instância:
    • emprestar(): Se o livro não estiver emprestado, mude emprestado para True e imprima uma mensagem. Caso contrário, imprima que o livro já está emprestado.
    • devolver(): Se o livro estiver emprestado, mude emprestado para False e imprima uma mensagem. Caso contrário, imprima que o livro não estava emprestado.
    • status(): Retorna uma string com o título, autor, número de páginas e se está emprestado ou não.
Dica: Como começar?

Pense no método __init__ para os atributos iniciais. Para os métodos emprestar e devolver, use condicionais (if/else) para verificar o estado de self.emprestado.

Checklist do Desafio 1:

  • Crie a classe Livro.
  • Implemente o método __init__ com titulo, autor, paginas e emprestado (com valor padrão).
  • Implemente o método emprestar().
  • Implemente o método devolver().
  • Implemente o método status().
  • Crie algumas instâncias de Livro.
  • Teste os métodos emprestar(), devolver() e status() para cada livro.
# Seu código para o Desafio 1 aqui
class Livro:
    def __init__(self, titulo, autor, paginas):
        self.titulo = titulo
        self.autor = autor
        self.paginas = paginas
        self.emprestado = False # Atributo de instância com valor padrão
 
    def emprestar(self):
        if not self.emprestado:
            self.emprestado = True
            print(f"'{self.titulo}' foi emprestado com sucesso.")
        else:
            print(f"'{self.titulo}' já está emprestado.")
 
    def devolver(self):
        if self.emprestado:
            self.emprestado = False
            print(f"'{self.titulo}' foi devolvido com sucesso.")
        else:
            print(f"'{self.titulo}' não estava emprestado.")
 
    def status(self):
        status_emprestimo = "Emprestado" if self.emprestado else "Disponível"
        return f"Livro: '{self.titulo}' por {self.autor}, {self.paginas} páginas. Status: {status_emprestimo}"
 
# Testando a classe Livro
print("\n--- Testando a classe Livro ---")
livro_a = Livro("O Pequeno Príncipe", "Antoine de Saint-Exupéry", 96)
livro_b = Livro("1984", "George Orwell", 328)
 
print(livro_a.status())
print(livro_b.status())
 
livro_a.emprestar()
print(livro_a.status())
 
livro_a.emprestar() # Tentando emprestar novamente
print(livro_a.status())
 
livro_b.devolver() # Tentando devolver algo que não está emprestado
print(livro_b.status())
 
livro_a.devolver()
print(livro_a.status())

Desafio 2: Classe Retangulo 📐

Crie uma classe Retangulo para calcular área e perímetro.

  • Atributos de Instância:
    • largura (float ou int)
    • altura (float ou int)
  • Métodos de Instância:
    • calcular_area(): Retorna a área do retângulo.
    • calcular_perimetro(): Retorna o perímetro do retângulo.
  • Método de Classe:
    • criar_quadrado(lado): Um método de classe que cria uma instância de Retangulo onde largura e altura são iguais ao lado fornecido.
Dica: Métodos de Classe

Lembre-se do decorador @classmethod e do parâmetro cls. Dentro do método, você pode usar return cls(lado, lado) para criar o quadrado.

Checklist do Desafio 2:

  • Crie a classe Retangulo.
  • Implemente o método __init__ com largura e altura.
  • Implemente o método calcular_area().
  • Implemente o método calcular_perimetro().
  • Implemente o método de classe criar_quadrado(lado).
  • Crie instâncias de Retangulo e Quadrado (usando o método de classe).
  • Teste todos os métodos.
# Seu código para o Desafio 2 aqui
class Retangulo:
    def __init__(self, largura, altura):
        if largura <= 0 or altura <= 0:
            raise ValueError("Largura e altura devem ser valores positivos.")
        self.largura = largura
        self.altura = altura
 
    def calcular_area(self):
        return self.largura * self.altura
 
    def calcular_perimetro(self):
        return 2 * (self.largura + self.altura)
 
    @classmethod
    def criar_quadrado(cls, lado):
        """
        Método de classe para criar um quadrado (um tipo especial de retângulo).
        """
        return cls(lado, lado) # cls() chama o construtor da própria classe
 
# Testando a classe Retangulo
print("\n--- Testando a classe Retangulo ---")
retangulo1 = Retangulo(10, 5)
print(f"Retângulo 1: Largura={retangulo1.largura}, Altura={retangulo1.altura}")
print(f"Área: {retangulo1.calcular_area()}")
print(f"Perímetro: {retangulo1.calcular_perimetro()}")
 
# Criando um quadrado usando o método de classe
quadrado1 = Retangulo.criar_quadrado(7)
print(f"\nQuadrado 1: Largura={quadrado1.largura}, Altura={quadrado1.altura}")
print(f"Área: {quadrado1.calcular_area()}")
print(f"Perímetro: {quadrado1.calcular_perimetro()}")
 
# Testando com valores inválidos (opcional, para explorar tratamento de erros)
try:
    Retangulo(0, 5)
except ValueError as e:
    print(f"\nErro ao criar retângulo: {e}")

Desafio 3: Classe ContaBancaria 💰

Crie uma classe ContaBancaria com funcionalidades de depósito e saque.

  • Atributo de Classe:
    • taxa_operacao (float, padrão 0.01 ou 1%)
  • Atributos de Instância:
    • numero_conta (string ou int)
    • saldo (float, padrão 0.0)
    • titular (string)
  • Métodos de Instância:
    • depositar(valor): Adiciona valor ao saldo. O valor deve ser positivo.
    • sacar(valor): Subtrai valor e a taxa_operacao (calculada sobre o valor sacado) do saldo. O valor deve ser positivo e o saldo deve ser suficiente.
    • extrato(): Retorna uma string com o numero_conta, titular e saldo atual.
  • Método Estático:
    • validar_valor(valor): Recebe um valor e retorna True se for um número positivo, False caso contrário. Use este método nos métodos depositar e sacar.
Dica: Métodos Estáticos e Atributos de Classe

Para a taxa_operacao, você pode acessá-la como ContaBancaria.taxa_operacao ou self.taxa_operacao (mas o primeiro é mais explícito para atributos de classe). O método estático validar_valor não precisa de self nem cls.

Checklist do Desafio 3:

  • Crie a classe ContaBancaria.
  • Defina o atributo de classe taxa_operacao.
  • Implemente o método __init__ com numero_conta, titular e saldo (com valor padrão).
  • Implemente o método depositar(valor).
  • Implemente o método sacar(valor).
  • Implemente o método extrato().
  • Implemente o método estático validar_valor(valor).
  • Use validar_valor dentro de depositar e sacar.
  • Crie instâncias de ContaBancaria.
  • Teste todos os métodos, incluindo cenários de saque inválido (saldo insuficiente, valor negativo).
# Seu código para o Desafio 3 aqui
class ContaBancaria:
    taxa_operacao = 0.01 # Atributo de classe: 1% de taxa por saque
 
    def __init__(self, numero_conta, titular, saldo=0.0):
        self.numero_conta = numero_conta
        self.titular = titular
        self.saldo = saldo
 
    @staticmethod
    def validar_valor(valor):
        """
        Método estático para validar se um valor é positivo.
        """
        return isinstance(valor, (int, float)) and valor > 0
 
    def depositar(self, valor):
        if not ContaBancaria.validar_valor(valor):
            print("Erro: O valor do depósito deve ser um número positivo.")
            return
 
        self.saldo += valor
        print(f"Depósito de R${valor:.2f} realizado. Saldo atual: R${self.saldo:.2f}")
 
    def sacar(self, valor):
        if not ContaBancaria.validar_valor(valor):
            print("Erro: O valor do saque deve ser um número positivo.")
            return
 
        valor_com_taxa = valor * (1 + ContaBancaria.taxa_operacao)
 
        if self.saldo >= valor_com_taxa:
            self.saldo -= valor_com_taxa
            print(f"Saque de R${valor:.2f} (taxa de R${valor * ContaBancaria.taxa_operacao:.2f}) realizado. Saldo atual: R${self.saldo:.2f}")
        else:
            print(f"Erro: Saldo insuficiente para sacar R${valor:.2f} (necessário R${valor_com_taxa:.2f}). Saldo atual: R${self.saldo:.2f}")
 
    def extrato(self):
        return f"Conta: {self.numero_conta} | Titular: {self.titular} | Saldo: R${self.saldo:.2f}"
 
# Testando a classe ContaBancaria
print("\n--- Testando a classe ContaBancaria ---")
conta1 = ContaBancaria("12345-6", "Maria Silva")
conta2 = ContaBancaria("78901-2", "João Pereira", 500.0)
 
print(conta1.extrato())
print(conta2.extrato())
 
conta1.depositar(1000)
conta1.sacar(50)
conta1.sacar(1000) # Tentando sacar mais do que tem
print(conta1.extrato())
 
conta2.depositar(200)
conta2.sacar(150)
print(conta2.extrato())
 
conta1.depositar(-50) # Testando valor inválido
conta1.sacar(0) # Testando valor inválido

6. Resumo e Próximos Passos 🚀

Parabéns! Você deu seus primeiros passos sólidos na Programação Orientada a Objetos em Python.

O que aprendemos hoje:

  • Classes: São moldes para criar objetos, definindo seus atributos e métodos.
  • Atributos de Instância: Dados únicos para cada objeto, definidos principalmente em __init__ usando self.
  • Atributos de Classe: Dados compartilhados por todos os objetos da classe, definidos diretamente no corpo da classe.
  • Métodos de Instância: Ações que um objeto pode realizar, operam nos atributos da instância e recebem self.
  • Métodos de Classe (@classmethod): Operam na classe, recebem cls, úteis para construtores alternativos ou para interagir com atributos de classe.
  • Métodos Estáticos (@staticmethod): Funções utilitárias relacionadas à classe, mas que não precisam de acesso à instância (self) nem à classe (cls).

Próximos Passos:

Com essa base sólida, estamos prontos para explorar outros pilares da POO:

  1. Herança: Como criar classes novas a partir de classes existentes, reutilizando código.
  2. Encapsulamento: Como proteger os dados de um objeto, controlando o acesso a eles.
  3. Polimorfismo: Como objetos de diferentes classes podem ser tratados de forma uniforme.

Continue praticando, e logo você estará construindo sistemas complexos e elegantes usando a POO! Até a próxima aula! 👋

© 2025 Escola All Dev. Todos os direitos reservados.

Atributos e Métodos: Criando Sua Primeira Classe - Fundamentos do Python para Iniciantes | escola.all.dev.br