9  Programação Orientada a Objetos em Python

De moldes de bolo para classes Python: organizando código como o mundo real

9.1 O que você saberá se ler todo este capítulo?

Prepare-se para sair do mundo das funções soltas e entrar no mundo da organização inteligente!

9.1.1 Você vai aprender:

  1. Por que POO existe - Quando usar orientação a objetos vs. programação procedural
  2. Classes e Objetos - Como criar “moldes” e “coisas” a partir desses moldes
  3. Atributos e Métodos - Características e ações dos seus objetos
  4. Construtor __init__ - Como inicializar objetos com dados específicos
  5. Acesso público e privado - Controlando quem pode ver o quê
  6. Herança - Como reutilizar código de classes existentes
  7. Polimorfismo - Como o mesmo método pode fazer coisas diferentes
  8. O poderoso self - A chave para entender POO em Python

9.1.2 Conceitos que você vai dominar:

  • Organização de código - Como estruturar programas complexos
  • Reutilização - Como evitar repetir código
  • Abstração - Como pensar em problemas do mundo real
  • Encapsulamento - Como proteger dados e comportamentos
  • Flexibilidade - Como criar código que se adapta facilmente

Dica importante: POO não é só para programadores experientes! É uma forma mais natural de pensar em problemas. Se você consegue organizar sua vida, consegue organizar código com POO!

9.2 O terror das universidades

Você sabia que programação orientada a objetos tira o sono de muita gente? Pois é. É um conceito que muita gente tem medo porque parece bem complexo. E, para ser bem sincero, pode ser explicado de um jeito bem difícil. Logo, o meu desafio aqui é que você entenda sem maiores dificuldades o que isso é e como isso funciona. Espero que você realmente entenda, e ficarei bem feliz em saber se cumpri (ou não) esta missão. Bora lá?

Um professor na Antártida ensinando algoritmos acompanhado de vários pinguins.
Um professor na Antártida ensinando algoritmos acompanhado de vários pinguins. Imagem gerada com o DALL-E 2.

9.3 Programação Orientada a Objetos (POO)

POO é uma forma de organizar código que imita como pensamos no mundo real. Em vez de criar funções soltas, criamos “coisas” que têm características e fazem ações - exatamente como tudo na vida real!

9.3.1 Analogia da Confeitaria

Voltando ao exemplo da confeitaria: imagine que você quer fazer bolos de chocolate. Todos os bolos de chocolate têm coisas em comum (ingredientes base) e fazem ações similares (assar, decorar).

Em POO, você criaria:

  • Classe BoloChocolate = A receita/moldes para fazer bolos de chocolate
  • Objetos = Os bolos individuais que você faz usando a receita
  • Atributos = As características de cada bolo (tamanho, sabor, decoração)
  • Métodos = As ações que cada bolo pode fazer (assar, esfriar, servir)

9.3.2 Analogia do Cinema

Agora imagine que você é um produtor de cinema. Cada filme tem personagens únicos, mas todos os filmes seguem uma estrutura similar:

  • Têm um protagonista, um antagonista, um enredo
  • Fazem ações como “trocar de cena”, “iniciar diálogo”
  • Cada filme é único, mas segue o mesmo “molde”

Vamos usar essa analogia para entender os conceitos principais de POO! 🎬

9.3.3 Classe: O Molde

Uma classe é como um molde ou receita. Você define o que toda “coisa” desse tipo deve ter, mas ainda não criou nenhuma “coisa” específica.

É como ter uma receita de bolo de chocolate: a receita diz quais ingredientes usar e quais passos seguir, mas você ainda não fez nenhum bolo real!

9.3.3.1 Exemplos de Classes:

Classe Filme:

  • Todo filme precisa ter: nome, protagonista, antagonista, ano de lançamento
  • É como um formulário em branco para cadastrar filmes

Classe SuperHeroi:

  • Todo super-herói precisa ter: nome, fraqueza, principal rival, superpoder
  • É como um formulário em branco para cadastrar super-heróis

Classe BoloChocolate:

  • Todo bolo de chocolate precisa ter: tamanho, quantidade de chocolate, decoração
  • É como uma receita base para fazer bolos de chocolate

9.3.4 Objeto: A Coisa Real

Agora que temos o molde (classe), vamos criar objetos reais! Um objeto é uma “coisa” específica criada a partir da classe.

É como usar a receita de bolo de chocolate para fazer bolos reais - cada bolo é único, mesmo seguindo a mesma receita!

9.3.4.1 Exemplos de Objetos:

Usando a Classe Filme:

Filme 1 Filme 2 Filme 3
Nome: “O Herói sem Nome” Nome: “A Busca pelo Tesouro” Nome: “O Mistério da Ilha”
Protagonista: Tom Protagonista: Michael Protagonista: Emily
Antagonista: John Antagonista: Robert Antagonista: Jake
Ano: 2015 Ano: 2012 Ano: 2022

Usando a Classe SuperHeroi:

Super-Herói 1 Super-Herói 2 Super-Herói 3
Nome: Tom Nome: Emily Nome: Michael
Fraqueza: Eletricidade Fraqueza: Intolerância à Lactose Fraqueza: Chuva
Rival: John Rival: Jake Rival: Robert
Superpoder: Velocidade Superpoder: Força Superpoder: Controle de Fogo

9.3.5 O que torna cada objeto único?

Cada objeto tem suas próprias características (atributos), mesmo que venha da mesma classe. É como ter vários bolos de chocolate - todos seguem a mesma receita, mas cada um pode ter tamanhos, decorações ou sabores diferentes!

9.3.6 Atributo: As Características

Atributos são as características ou propriedades de um objeto. São como os “dados” que cada objeto possui.

9.3.6.1 Analogia do Bolo:

  • Atributos do bolo: fá, decoração, quantidade de chocolate, temperatura
  • Cada bolo pode ter valores diferentes para esses atributos

9.3.6.2 Analogia do Filme:

  • Atributos do filme: nome, protagonista, antagonista, ano de lançamento
  • Cada filme tem valores únicos para esses atributos

9.3.6.3 Resumo dos Atributos:

Classe Atributos
Filme nome, protagonista, antagonista, ano
SuperHeroi nome, fraqueza, rival, superpoder
BoloChocolate tamanho, decoração, quantidade_chocolate

9.3.7 Por que atributos são importantes?

Os atributos permitem que objetos da mesma classe sejam únicos. Dois filmes podem ter o mesmo nome, mas diferentes protagonistas ou anos de lançamento!

9.3.8 Métodos: As Ações

Métodos são as ações ou comportamentos que um objeto pode realizar. São como funções que pertencem a uma classe específica.

9.3.8.1 Analogia do Bolo:

  • Métodos do bolo: assar(), esfriar(), decorar(), servir()
  • Cada bolo pode realizar essas ações

9.3.8.2 Analogia do Filme:

  • Métodos do filme: trocar_cena(), iniciar_dialogo(), finalizar_filme()
  • Cada filme pode executar essas ações

9.3.8.3 Analogia do Super-Herói:

  • Métodos do super-herói: voar(), lutar(), salvar_pessoa(), falar()
  • Cada super-herói pode realizar essas ações

9.3.9 Exemplos de Métodos por Classe:

Classe Métodos (Ações)
Filme trocar_cena(), iniciar_dialogo(), finalizar_filme()
SuperHeroi voar(), lutar(), salvar_princessa(), falar()
BoloChocolate assar(), esfriar(), decorar(), servir()

9.3.10 O que métodos fazem?

Métodos podem:

  • Alterar atributos do objeto (ex: assar() muda a temperatura do bolo)
  • Retornar valores (ex: calcular_area() retorna um número)
  • Executar ações (ex: trocar_cena() executa uma mudança)
  • Interagir com outros objetos (ex: lutar() pode afetar outro super-herói)

9.3.11 Acesso Público e Privado

Em POO, você pode controlar quem pode “ver” e “usar” os atributos e métodos dos seus objetos. É como ter uma casa com quartos públicos (sala, cozinha) e quartos privados (quarto, banheiro)!

9.3.11.1 Analogia da Casa:

Atributos/Métodos Públicos = Quartos que qualquer visitante pode ver:

  • Sala de estar (todos podem entrar)
  • Cozinha (todos podem usar)
  • Jardim (todos podem ver)

Atributos/Métodos Privados = Quartos que só o dono da casa pode usar:

  • Quarto pessoal (só o dono entra)
  • Banheiro (só o dono usa)
  • Escritório (só o dono tem acesso)

9.3.11.2 Analogia do Filme:

Público = O que o público vê na tela:

  • Nome do personagem
  • Cor do cabelo
  • Ações como “lutar” ou “falar”

Privado = O que fica “por trás das câmeras”:

  • Pensamentos do personagem
  • Segredos do personagem
  • Ações internas como “pensar” ou “lembrar”

9.3.11.3 Como funciona em Python:

class Pessoa:
    def __init__(self, nome, idade):
        self.nome = nome          # Público (qualquer um pode ver)
        self.__idade = idade      # Privado (só a própria classe pode ver)
    
    def falar(self):              # Público (qualquer um pode chamar)
        return f"Olá, sou {self.nome}"
    
    def __pensar(self):           # Privado (só a própria classe pode chamar)
        return "Estou pensando..."

9.3.12 Por que usar público e privado?

  • Segurança: Protege dados importantes
  • Organização: Separa o que é interno do que é externo
  • Controle: Você decide como outros objetos podem interagir com o seu

9.3.13 Herança: Reutilizando Código

Herança é como uma família em POO! Uma classe “filha” herda características e comportamentos de uma classe “pai”, mas pode ter suas próprias características especiais.

9.3.13.1 Analogia da Confeitaria:

Imagine que você tem uma receita base de bolo que funciona para vários tipos:

  • Receita Base (Classe Pai): ingredientes básicos, temperatura do forno, tempo de cozimento
  • Receita de Bolo de Chocolate (Classe Filha): herda tudo da base + adiciona chocolate
  • Receita de Bolo de Morango (Classe Filha): herda tudo da base + adiciona morango

9.3.13.2 Analogia do Cinema:

Classe Pai Filme:

  • Atributos: nome, protagonista, antagonista, ano
  • Métodos: trocar_cena(), iniciar_dialogo(), finalizar_filme()

Classe Filha FilmeEspacial:

  • Herda TUDO da classe Filme
  • Adiciona: ano_futuro, tem_alienigenas
  • Adiciona métodos: iniciar_batalha_espacial(), pousar_planeta()

9.3.13.3 Analogia dos Animais:

Classe Pai Animal:

  • Atributos: peso, tamanho, cor
  • Métodos: comer(), dormir(), respirar()

Classe Filha Cachorro:

  • Herda TUDO da classe Animal
  • Adiciona métodos: latir(), buscar_objeto(), abanar_rabo()

Classe Filha Gato:

  • Herda TUDO da classe Animal
  • Adiciona métodos: miar(), escalar(), ronronar()

9.3.14 Vantagens da Herança:

  1. Reutilização: Não precisa reescrever código
  2. Organização: Código mais limpo e organizado
  3. Manutenção: Mudanças na classe pai afetam todas as filhas
  4. Extensibilidade: Fácil adicionar novos tipos

9.3.15 Resumo da Herança:

Classe Pai Classe Filha O que herda O que adiciona
Filme FilmeEspacial Tudo do Filme Atributos/métodos espaciais
Animal Cachorro Tudo do Animal latir(), buscar_objeto()
Animal Gato Tudo do Animal miar(), escalar()

9.3.16 Polimorfismo: Uma Ação, Muitas Formas

Polimorfismo significa “muitas formas”. É quando o mesmo método pode se comportar de formas diferentes dependendo do tipo de objeto que o chama.

9.3.16.1 Analogia do Rio:

Imagine que vários personagens precisam atravessar um rio:

  • Nadador: usa o método atravessar_rio() nadando
  • Corredor: usa o método atravessar_rio() pulando pedras
  • Piloto: usa o método atravessar_rio() voando com helicóptero

Todos usam o mesmo método, mas cada um executa de forma diferente!

9.3.16.2 Analogia dos Animais:

Método fazer_som():

  • Cachorro: fazer_som() → “Au au!”
  • Gato: fazer_som() → “Miau!”
  • Galinha: fazer_som() → “Cocoricó!”

O mesmo método, mas cada animal responde diferente!

9.3.16.3 Analogia da Confeitaria:

Método assar():

  • Bolo de Chocolate: assar() → 180°C por 30 minutos
  • Bolo de Cenoura: assar() → 170°C por 25 minutos
  • Bolo de Fubá: assar() → 190°C por 35 minutos

O mesmo método, mas cada bolo tem sua receita específica!

9.3.17 Vantagens do Polimorfismo:

  1. Flexibilidade: Um código pode funcionar com diferentes tipos
  2. Reutilização: Não precisa reescrever código para cada tipo
  3. Extensibilidade: Fácil adicionar novos tipos sem quebrar código existente
  4. Simplicidade: Código mais limpo e fácil de entender

9.3.18 Exemplo Prático:

# Todos os animais podem fazer som, mas cada um de forma diferente
animais = [Cachorro(), Gato(), Galinha()]

for animal in animais:
    animal.fazer_som()  # Cada um faz som diferente!

9.3.19 Resumo do Polimorfismo:

Método Cachorro Gato Galinha
fazer_som() “Au au!” “Miau!” “Cocoricó!”
dormir() Deita no chão Enrosca no sofá Pousa no poleiro
comer() Come ração Come peixe Come milho

9.4 Resumo do Capítulo

Agora você domina os conceitos fundamentais de POO! Vamos fazer um resumo rápido:

9.4.1 Conceitos Principais:

Conceito O que é Analogia
Classe Molde para criar objetos Receita de bolo
Objeto Coisa específica criada da classe Bolo real feito da receita
Atributo Características do objeto Ingredientes do bolo
Método Ações que o objeto pode fazer Etapas da receita
Público Qualquer um pode ver/usar Ingredientes visíveis
Privado Só a própria classe pode ver/usar Ingrediente secreto
Herança Classe filha herda da classe pai Receita adaptada de outra
Polimorfismo Mesmo método, comportamentos diferentes Mesmo passo, formas diferentes

9.4.2 Quando usar POO?

  • Programas complexos (mais de 100 linhas)
  • Múltiplas “coisas” similares (vários usuários, produtos, etc.)
  • Comportamentos complexos (cada “coisa” faz várias ações)
  • Reutilização de código (evitar repetir código)
  • Manutenção fácil (fácil de modificar e expandir)

9.4.3 Vantagens da POO:

  1. Organização: Código mais limpo e estruturado
  2. Reutilização: Não precisa reescrever código
  3. Manutenção: Fácil de modificar e expandir
  4. Abstração: Pensa em problemas do mundo real
  5. Flexibilidade: Código que se adapta facilmente

🎯 Parabéns! Você agora entende os conceitos fundamentais de POO! No próximo capítulo, vamos colocar isso em prática com código Python real. Prepare-se para criar suas próprias classes e objetos! 🚀

9.5 POO em Python - Hora da Prática!

Agora que você já entendeu os conceitos básicos de POO, vamos colocar a mão na democratura! 🎯

💡 Dica: Existem outros conceitos como interfaces e classes abstratas, mas vamos focar no essencial primeiro. Romeu e Julieta não se casaram no primeiro encontro, né? 😉

9.5.1 Classes, Construtores, Atributos e Métodos

Vamos começar com um exemplo bem simples. Teste este código no PyCharm - pode parecer que não acontece nada, mas é só o começo! 🚀

class Pessoa:
    def __init__(self, nome_informado, idade_informada):
        self.nome = nome_informado
        self.idade = idade_informada

Vamos analisar esse código linha por linha! 🔍

Linha 1: class Pessoa: - Aqui criamos nossa classe com a palavra-chave class (sem aspas, sem frescura!)

Linha 2: def __init__(self, nome_informado, idade_informada): - Agora temos um método especial chamado __init__. Em POO, não chamamos mais de “função”, mas sim de método. E esse __init__ é especial - é o construtor! Ele roda automaticamente toda vez que criamos um novo objeto.

Linhas 3-4: Definimos dois atributos (nome e idade) usando self. O self é como o “eu” do objeto - ele se refere ao objeto atual que está sendo criado.

🤔 Pense assim: É como se cada pessoa dissesse “meu nome é…” e “minha idade é…”. O self é esse “meu”!

Resumindo: Criamos um molde para fazer pessoas, mas ainda não fizemos nenhuma pessoa real. É como ter uma receita de bolo sem ter assado nenhum bolo ainda! 🍰

9.5.2 Objetos - Hora de Criar Pessoas Reais!

Agora vamos usar nosso molde para criar pessoas de verdade! Vamos criar 3 pessoas usando a classe Pessoa. Teste este código:

pessoa1 = Pessoa("Maria", 25)
pessoa2 = Pessoa("João", 30)
pessoa3 = Pessoa("Pedro", 20)

print(pessoa1.nome)
print(pessoa3.idade)

A saída será isto:

Maria
20

Analisando o código:

  1. Criamos 3 pessoas: Cada linha cria uma pessoa nova usando a classe Pessoa
  2. Passamos os parâmetros: Lembra que nosso construtor pede nome e idade? Então passamos na mesma ordem!
  3. Armazenamos em variáveis: pessoa1, pessoa2 e pessoa3 são como “etiquetas” para cada pessoa

Acessando os atributos:

  • pessoa1.nome - Pega o nome da primeira pessoa
  • pessoa3.idade - Pega a idade da terceira pessoa
  • Sempre usamos o ponto (.) para acessar atributos dentro do objeto

🎯 Dica: É como abrir uma gaveta específica de uma pessoa específica! pessoa1.nome é como abrir a gaveta “nome” da pessoa 1! 📁

9.5.3 Métodos - Ações dos Objetos!

Até agora vimos classes, atributos e objetos. Agora vamos ver os métodos - as ações que nossos objetos podem fazer! 🎭

Vamos criar uma classe Circulo que tem:

  • 3 atributos: raio, cor da borda, cor do preenchimento
  • 4 métodos: calcular área, calcular perímetro, mudar cores, redimensionar raio

É como dar superpoderes para nossos círculos! 🦸‍♂️

import math

class Circulo:
    def __init__(self, raio_circulo, cor_borda, cor_preenchimento):
        self.raio = raio_circulo
        self.cor_borda = cor_borda
        self.cor_preenchimento = cor_preenchimento

    def calcular_area(self):
        return math.pi * self.raio ** 2

    def calcular_perimetro(self):
        return 2 * math.pi * self.raio

    def mudar_cor(self, nova_cor_borda, nova_cor_preenchimento):
        self.cor_borda = nova_cor_borda
        self.cor_preenchimento = nova_cor_preenchimento

    def redimensionar_raio(self, novo_raio):
        self.raio = novo_raio

Analisando o código:

  1. import math - Importamos o módulo math para usar π (pi). É mais fácil que digitar 3.1415… na mão! 😅

  2. Construtor __init__ - Recebe 3 atributos: raio, cor_borda e cor_preenchimento

  3. Métodos:

    • calcular_area() - Calcula a área usando π × raio²
    • calcular_perimetro() - Calcula o perímetro usando 2 × π × raio
    • mudar_cor() - Permite trocar as cores do círculo
    • redimensionar_raio() - Permite mudar o tamanho do círculo

🤔 Dica importante: O self sempre é o primeiro parâmetro! É como o objeto dizendo “eu vou fazer isso comigo mesmo!” 🎯

O que cada método faz:

  • calcular_area() - Faz a matemática: π × raio²
  • calcular_perimetro() - Faz a matemática: 2 × π × raio
  • mudar_cor() - Permite trocar as cores (como pintar o círculo de novo!)
  • redimensionar_raio() - Permite mudar o tamanho (como esticar ou encolher!)

Agora vamos criar 3 círculos diferentes! Cada um com seu próprio tamanho e cores! 🎨

circulo1 = Circulo(3, "amarelo", "azul")
circulo2 = Circulo(5, "verde", "verde")
circulo3 = Circulo(2, "azul", "roxo")

Criamos 3 círculos únicos! Cada um com seu próprio raio e cores. Agora vamos usar os métodos para fazer cálculos e mudanças! 🎯

print(f'O raio do círculo 3 é {circulo3.raio}')
print(f'A área do círculo 3 é {circulo3.calcular_area()}')

circulo3.redimensionar_raio(4)
print(f'O novo raio do círculo 3 é {circulo3.raio}')
print(f'A nova área do círculo 3 é {circulo3.calcular_area()}')

print(circulo1.cor_borda, circulo1.cor_preenchimento)
print(circulo2.calcular_perimetro())

O resultado deve ser o seguinte:

O raio do círculo 3 é 2
A área do círculo 3 é 12.566370614359172
O novo raio do círculo 3 é 4
A nova área do círculo 3 é 50.26548245743669
amarelo azul
31.41592653589793

Pontos importantes para lembrar:

  1. Acessando atributos: objeto.atributo (sem parênteses!)
    • circulo3.raio
    • circulo3.raio()
  2. Chamando métodos: objeto.metodo() (com parênteses!)
    • circulo3.calcular_area()
    • circulo3.calcular_area
  3. Métodos vs Atributos:
    • Atributos são características (raio, cor)
    • Métodos são ações (calcular, mudar)
  4. O objeto “lembra” de si mesmo: Quando chamamos calcular_area(), o método usa automaticamente o raio que já está guardado no objeto. É como se o círculo dissesse “eu sei meu próprio raio, não preciso que você me diga!” 🤖

💡 Dica: É como perguntar para uma pessoa “qual sua idade?” vs “calcule sua idade”. A primeira é um atributo, a segunda é uma ação! 🎯

9.5.4 Acesso Público e Privado - Controlando o Acesso!

Até agora todos nossos atributos e métodos eram públicos (qualquer um podia ver e usar). Mas e se quisermos criar alguns privados? É como ter quartos na sua casa - alguns são para visitantes, outros só para você! 🏠

import math

class Circulo:
    def __init__(self, raio_circulo, cor_borda, cor_preenchimento):
        self.__raio = raio_circulo
        self.cor_borda = cor_borda
        self.cor_preenchimento = cor_preenchimento

    def calcular_area(self):
        return math.pi * self.__raio ** 2

    def calcular_perimetro(self):
        return 2 * math.pi * self.__raio

    def mudar_cor(self, nova_cor_borda, nova_cor_preenchimento):
        self.cor_borda = nova_cor_borda
        self.cor_preenchimento = nova_cor_preenchimento

    def redimensionar_raio(self, novo_raio):
        self.__raio = novo_raio

    def __metodo_privado(self):
        print("Este é um método privado.")

Analisando o código:

  1. __raio - O atributo raio agora é privado (dois underscores no início)
  2. __metodo_privado() - O método também é privado (dois underscores no início)

Como funciona:

  • Público = Qualquer um pode ver e usar (como a sala da sua casa)
  • Privado = Só a própria classe pode usar (como seu quarto pessoal)

🤔 Importante: Os dois underscores (__) são como uma placa de “Privado” - é uma recomendação, não uma trava de segurança! É como dizer “por favor, não entre aqui” mas ainda é possível entrar se alguém realmente quiser! 😅

Por que usar privado?

  • Organização: Separa o que é interno do que é externo
  • Segurança: Protege dados importantes
  • Controle: Você decide como outros objetos podem interagir

9.5.5 Herança e Polimorfismo - Família de Classes!

Agora vamos para os conceitos mais avançados! O círculo não deixa de ser uma forma, né? Assim como o retângulo também é. Vamos criar uma família de formas! 👨‍👩‍👧‍👦

Teste o código abaixo no PyCharm:

import math

class Forma:
    def calcular_area(self):
        return "Ainda não tenho uma lógica implementada para calcular esta área."
    
    def calcular_perimetro(self):
        return "Ainda não tenho uma lógica implementada para calcular este perímetro."

class Circulo(Forma):
    def __init__(self, raio):
        self.raio = raio
    
    def calcular_perimetro(self):
        return 2 * math.pi * self.raio
    
    def redimensionar_raio(self, novo_raio):
        self.raio = novo_raio

class Retangulo(Forma):
    def __init__(self, base, altura):
        self.base = base
        self.altura = altura
    
    def redimensionar_base(self, nova_base):
        self.base = nova_base
        
    def redimensionar_altura(self, nova_altura):
        self.altura = nova_altura
        
circulo1 = Circulo(3)
retangulo1 = Retangulo(10, 2)

print(circulo1.calcular_area())
print(circulo1.calcular_perimetro())

print(retangulo1.calcular_area())
print(retangulo1.calcular_perimetro())

9.5.5.1 Herança - Pai e Filhos!

Como funciona a herança:

  1. Classe Pai Forma - É a classe base que tem os métodos comuns
  2. Classes Filhas - Circulo e Retangulo herdam tudo da classe pai
  3. Sintaxe: class Circulo(Forma) - O (Forma) diz que Circulo herda de Forma

O que cada classe tem:

Classe Construtor Métodos Especiais
Forma Nenhum calcular_area(), calcular_perimetro()
Circulo raio redimensionar_raio()
Retangulo base, altura redimensionar_base(), redimensionar_altura()

🎯 Dica: É como uma família! O pai ensina coisas básicas para os filhos, mas cada filho pode ter suas próprias habilidades especiais! 👨‍👩‍👧‍👦

Ao rodar o código, o seguinte resultado aparecerá:

Ainda não tenho uma lógica implementada para calcular esta área.
18.84955592153876
Ainda não tenho uma lógica implementada para calcular esta área.
Ainda não tenho uma lógica implementada para calcular este perímetro.

Analisando os resultados:

  • Linha 1: circulo1.calcular_area() → Retorna mensagem da classe pai Forma
  • Linha 2: circulo1.calcular_perimetro() → Retorna valor numérico (círculo implementou!)
  • Linha 3: retangulo1.calcular_area() → Retorna mensagem da classe pai Forma
  • Linha 4: retangulo1.calcular_perimetro() → Retorna mensagem da classe pai Forma

O que aconteceu:

  1. Circulo implementou seu próprio calcular_perimetro() (por isso retornou valor numérico)
  2. Retangulo não implementou, então usou o da classe pai Forma (por isso retornou mensagem)
  3. Ambos não implementaram calcular_area(), então usaram o da classe pai

💡 Dica: É como herdar habilidades! Se o filho não aprende uma habilidade específica, ele usa a habilidade básica do pai! 🎯

9.5.5.2 Polimorfismo - Uma Ação, Muitas Formas!

Agora vamos implementar o polimorfismo! É quando o mesmo método se comporta de formas diferentes dependendo do tipo de objeto! 🎭

Vamos alterar o código para implementar a lógica de calcular_perimetro() e calcular_area() para as duas classes:

import math

class Forma:
    def calcular_area(self):
        return "Ainda não tenho uma lógica implementada para calcular esta área."
    
    def calcular_perimetro(self):
        return "Ainda não tenho uma lógica implementada para calcular este perímetro."

class Circulo(Forma):
    def __init__(self, raio):
        self.raio = raio
    
    def calcular_area(self):
        return math.pi * self.raio ** 2

    def calcular_perimetro(self):
        return 2 * math.pi * self.raio
    
    def redimensionar_raio(self, novo_raio):
        self.raio = novo_raio

class Retangulo(Forma):
    def __init__(self, base, altura):
        self.base = base
        self.altura = altura
    
    def calcular_area(self):
        return self.base * self.altura

    def calcular_perimetro(self):
        return 2 * (self.base + self.altura)
    
    def redimensionar_base(self, nova_base):
        self.base = nova_base
        
    def redimensionar_altura(self, nova_altura):
        self.altura = nova_altura
        
circulo1 = Circulo(3)
retangulo1 = Retangulo(10, 2)

print(circulo1.calcular_area())
print(circulo1.calcular_perimetro())

print(retangulo1.calcular_area())
print(retangulo1.calcular_perimetro())

Se você rodar este código verá que teremos o seguinte resultado:

28.274333882308138
18.84955592153876
20
24

Consegue ver que tanto para o círculo quanto para o retângulo pedimos pelo mesmo nome do método? Ou seja: para ambos, chamamos o calcular_area() e calcular_retangulo(). Por outro lado, os dois possuem lógicas de implementação totalmente diferentes. Isto é um exemplo prático de polimorfismo.

Quer outro exemplo de polimorfismo que já existe no Python? O operador +. Veja que ele consegue somar números (2 + 2 será igual a 4), mas ele também funciona para string ("Teste" + "Chinelo" será igual a TesteChinelo). Perceba que o mesmo operador tem comportamentos diferentes para tipos de dados diferentes: isto também é polimorfismo.

9.6 Exercícios - Programação Orientada a Objetos

9.6.0.1 Exercício 1: Classe Animal Básica

Enunciado: Crie uma classe Animal com atributos nome e especie. Crie um método fazer_som() que mostra o som do animal.

9.6.0.2 Exercício 2: Classe Pessoa

Enunciado: Crie uma classe Pessoa com nome e idade. Crie um método apresentar() que mostra “Olá, eu sou [nome] e tenho [idade] anos”.

9.6.0.3 Exercício 3: Classe Retângulo

Enunciado: Crie uma classe Retangulo com largura e altura. Crie um método calcular_area() que retorna a área.

9.6.0.4 Exercício 4: Classe Carro

Enunciado: Crie uma classe Carro com marca e modelo. Crie um método ligar() que mostra “O [marca] [modelo] está ligado!”.

9.6.0.5 Exercício 5: Classe Livro

Enunciado: Crie uma classe Livro com título e autor. Crie um método mostrar_info() que mostra as informações do livro.

9.6.0.6 Exercício 6: Classe Calculadora

Enunciado: Crie uma classe Calculadora com métodos para somar, subtrair, multiplicar e dividir.

9.6.0.7 Exercício 7: Classe Funcionário

Enunciado: Crie uma classe Funcionario com nome e salário. Crie um método calcular_salario_anual() que inclui o 13º salário.

9.6.0.8 Exercício 8: Classe Estudante

Enunciado: Crie uma classe Estudante com nome, idade e nota. Crie um método calcular_media() que recebe outro estudante e calcula a média das notas.

9.6.0.9 Exercício 9: Classe Círculo

Enunciado: Crie uma classe Circulo com raio. Crie métodos para calcular área e perímetro.

9.6.0.10 Exercício 10: Classe Conta Bancária

Enunciado: Crie uma classe ContaBancaria com número da conta e saldo. Crie métodos para depositar e sacar dinheiro.

9.7 Respostas dos Exercícios

Resposta:```python class Animal: def init(self, nome, especie): self.nome = nome self.especie = especie

def fazer_som(self):
    print(f"{self.nome} está fazendo um som de {self.especie}")

10 Criar objeto e testar

meu_animal = Animal(“Rex”, “cachorro”) meu_animal.fazer_som()


**Explicação linha por linha:**

- **Linha 1:** `class Animal:` - Define a classe Animal
- **Linha 2:** `def __init__(self, nome, especie):` - Define o construtor
- **Linhas 3-4:** Define os atributos nome e especie
- **Linha 6:** `def fazer_som(self):` - Define o método fazer_som
- **Linha 7:** `print(f"{self.nome} está fazendo um som de {self.especie}")` - Mostra o som
- **Linha 10:** `meu_animal = Animal("Rex", "cachorro")` - Cria objeto
- **Linha 11:** `meu_animal.fazer_som()` - Chama o método

**Resposta:**```python
class Pessoa:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
    
    def apresentar(self):
        print(f"Olá, eu sou {self.nome} e tenho {self.idade} anos")

# Criar pessoa e apresentar
pessoa = Pessoa("Ana", 25)
pessoa.apresentar()

Explicação linha por linha:

  • Linha 1: class Pessoa: - Define a classe Pessoa
  • Linha 2: def __init__(self, nome, idade): - Define o construtor
  • Linhas 3-4: Define os atributos nome e idade
  • Linha 6: def apresentar(self): - Define o método apresentar
  • Linha 7: print(f"Olá, eu sou {self.nome} e tenho {self.idade} anos") - Mostra apresentação
  • Linha 10: pessoa = Pessoa("Ana", 25) - Cria objeto
  • Linha 11: pessoa.apresentar() - Chama o método

Resposta:```python class Retangulo: def init(self, largura, altura): self.largura = largura self.altura = altura

def calcular_area(self):
    return self.largura * self.altura

11 Criar retângulo e calcular área

retangulo = Retangulo(5, 3) area = retangulo.calcular_area() print(f”A área do retângulo é {area}“)


**Explicação linha por linha:**

- **Linha 1:** `class Retangulo:` - Define a classe Retangulo
- **Linha 2:** `def __init__(self, largura, altura):` - Define o construtor
- **Linhas 3-4:** Define os atributos largura e altura
- **Linha 6:** `def calcular_area(self):` - Define o método calcular_area
- **Linha 7:** `return self.largura * self.altura` - Retorna a área
- **Linha 10:** `retangulo = Retangulo(5, 3)` - Cria objeto
- **Linha 11:** `area = retangulo.calcular_area()` - Calcula área
- **Linha 12:** `print(f"A área do retângulo é {area}")` - Mostra resultado

**Resposta:**```python
class Carro:
    def __init__(self, marca, modelo):
        self.marca = marca
        self.modelo = modelo
    
    def ligar(self):
        print(f"O {self.marca} {self.modelo} está ligado!")

# Criar carro e ligar
meu_carro = Carro("Toyota", "Corolla")
meu_carro.ligar()

Explicação linha por linha:

  • Linha 1: class Carro: - Define a classe Carro
  • Linha 2: def __init__(self, marca, modelo): - Define o construtor
  • Linhas 3-4: Define os atributos marca e modelo
  • Linha 6: def ligar(self): - Define o método ligar
  • Linha 7: print(f"O {self.marca} {self.modelo} está ligado!") - Mostra mensagem
  • Linha 10: meu_carro = Carro("Toyota", "Corolla") - Cria objeto
  • Linha 11: meu_carro.ligar() - Chama o método

Resposta:```python

Resposta:```python class Calculadora: def somar(self, a, b): return a + b

def subtrair(self, a, b):
    return a - b

def multiplicar(self, a, b):
    return a * b

def dividir(self, a, b):
    if b == 0:
        return "Erro: divisão por zero!"
    return a / b

12 Criar calculadora e testar

calc = Calculadora() print(calc.somar(5, 3)) # 8 print(calc.dividir(10, 2)) # 5.0 print(calc.dividir(10, 0)) # Erro


**Explicação linha por linha:**

- **Linha 1:** `class Calculadora:` - Define a classe Calculadora
- **Linha 2:** `def somar(self, a, b):` - Define método somar
- **Linha 3:** `return a + b` - Retorna soma
- **Linha 5:** `def subtrair(self, a, b):` - Define método subtrair
- **Linha 6:** `return a - b` - Retorna subtração
- **Linha 8:** `def multiplicar(self, a, b):` - Define método multiplicar
- **Linha 9:** `return a * b` - Retorna multiplicação
- **Linha 11:** `def dividir(self, a, b):` - Define método dividir
- **Linhas 12-13:** Verifica divisão por zero
- **Linha 14:** `return a / b` - Retorna divisão

**Resposta:**```python
class Funcionario:
    def __init__(self, nome, salario):
        self.nome = nome
        self.salario = salario
    
    def calcular_salario_anual(self):
        return self.salario * 13
    
    def mostrar_info(self):
        print(f"Funcionário: {self.nome}")
        print(f"Salário mensal: R$ {self.salario:.2f}")
        print(f"Salário anual: R$ {self.calcular_salario_anual():.2f}")

# Criar funcionário e mostrar informações
func = Funcionario("João", 3000)
func.mostrar_info()

Explicação linha por linha:

  • Linha 1: class Funcionario: - Define a classe Funcionario
  • Linha 2: def __init__(self, nome, salario): - Define o construtor
  • Linhas 3-4: Define os atributos nome e salario
  • Linha 6: def calcular_salario_anual(self): - Define método
  • Linha 7: return self.salario * 13 - Retorna salário anual
  • Linha 9: def mostrar_info(self): - Define método mostrar_info
  • Linhas 10-12: Mostra informações do funcionário
  • Linha 15: func = Funcionario("João", 3000) - Cria objeto
  • Linha 16: func.mostrar_info() - Chama o método

Resposta:```python class Estudante: def init(self, nome, idade, nota): self.nome = nome self.idade = idade self.nota = nota

def calcular_media(self, outro_estudante):
    return (self.nota + outro_estudante.nota) / 2

def mostrar_info(self):
    print(f"Estudante: {self.nome}")
    print(f"Idade: {self.idade} anos")
    print(f"Nota: {self.nota}")

13 Criar estudantes e calcular média

est1 = Estudante(“Ana”, 20, 8.5) est2 = Estudante(“João”, 19, 7.5) media = est1.calcular_media(est2) print(f”Média das notas: {media}“)


**Explicação linha por linha:**

- **Linha 1:** `class Estudante:` - Define a classe Estudante
- **Linha 2:** `def __init__(self, nome, idade, nota):` - Define o construtor
- **Linhas 3-5:** Define os atributos nome, idade e nota
- **Linha 7:** `def calcular_media(self, outro_estudante):` - Define método
- **Linha 8:** `return (self.nota + outro_estudante.nota) / 2` - Calcula média
- **Linha 10:** `def mostrar_info(self):` - Define método mostrar_info
- **Linhas 11-13:** Mostra informações do estudante
- **Linha 16:** `est1 = Estudante("Ana", 20, 8.5)` - Cria primeiro estudante
- **Linha 17:** `est2 = Estudante("João", 19, 7.5)` - Cria segundo estudante
- **Linha 18:** `media = est1.calcular_media(est2)` - Calcula média
- **Linha 19:** `print(f"Média das notas: {media}")` - Mostra resultado

**Resposta:**```python
import math

class Circulo:
    def __init__(self, raio):
        self.raio = raio
    
    def calcular_area(self):
        return math.pi * self.raio ** 2
    
    def calcular_perimetro(self):
        return 2 * math.pi * self.raio
    
    def mostrar_info(self):
        print(f"Raio: {self.raio}")
        print(f"Área: {self.calcular_area():.2f}")
        print(f"Perímetro: {self.calcular_perimetro():.2f}")

# Criar círculo e mostrar informações
circulo = Circulo(5)
circulo.mostrar_info()

Explicação linha por linha:

  • Linha 1: import math - Importa módulo math para usar π
  • Linha 3: class Circulo: - Define a classe Circulo
  • Linha 4: def __init__(self, raio): - Define o construtor
  • Linha 5: self.raio = raio - Define o atributo raio
  • Linha 7: def calcular_area(self): - Define método calcular_area
  • Linha 8: return math.pi * self.raio ** 2 - Retorna área
  • Linha 10: def calcular_perimetro(self): - Define método calcular_perimetro
  • Linha 11: return 2 * math.pi * self.raio - Retorna perímetro
  • Linha 13: def mostrar_info(self): - Define método mostrar_info
  • Linhas 14-16: Mostra informações do círculo
  • Linha 19: circulo = Circulo(5) - Cria objeto
  • Linha 20: circulo.mostrar_info() - Chama o método

Resposta:```python class ContaBancaria: def init(self, numero_conta, saldo_inicial=0): self.numero_conta = numero_conta self.saldo = saldo_inicial

def depositar(self, valor):
    if valor > 0:
        self.saldo += valor
        print(f"Depósito de R$ {valor:.2f} realizado!")
    else:
        print("Valor inválido para depósito!")

def sacar(self, valor):
    if valor > 0 and valor <= self.saldo:
        self.saldo -= valor
        print(f"Saque de R$ {valor:.2f} realizado!")
    else:
        print("Saldo insuficiente ou valor inválido!")

def mostrar_saldo(self):
    print(f"Saldo atual: R$ {self.saldo:.2f}")

14 Criar conta e testar operações

conta = ContaBancaria(“12345”, 1000) conta.depositar(500) conta.sacar(200) conta.mostrar_saldo()


**Explicação linha por linha:**

- **Linha 1:** `class ContaBancaria:` - Define a classe ContaBancaria
- **Linha 2:** `def __init__(self, numero_conta, saldo_inicial=0):` - Define construtor
- **Linhas 3-4:** Define os atributos numero_conta e saldo
- **Linha 6:** `def depositar(self, valor):` - Define método depositar
- **Linhas 7-9:** Verifica valor e deposita
- **Linhas 10-11:** Trata valor inválido
- **Linha 13:** `def sacar(self, valor):` - Define método sacar
- **Linhas 14-16:** Verifica valor e saldo, saca
- **Linhas 17-18:** Trata erro
- **Linha 20:** `def mostrar_saldo(self):` - Define método mostrar_saldo
- **Linha 21:** `print(f"Saldo atual: R$ {self.saldo:.2f}")` - Mostra saldo


### Nível 3: Médio (5 exercícios)

#### Exercício 11: Classe com Atributos Privados
**Enunciado:** Crie uma classe `Pessoa` com nome (público) e idade (privado). Crie métodos para definir e obter a idade.

**Resposta:**
```python
class Pessoa:
    def __init__(self, nome, idade):
        self.nome = nome
        self.__idade = idade  # Atributo privado
    
    def definir_idade(self, nova_idade):
        if nova_idade >= 0:
            self.__idade = nova_idade
        else:
            print("Idade inválida!")
    
    def obter_idade(self):
        return self.__idade
    
    def mostrar_info(self):
        print(f"Nome: {self.nome}")
        print(f"Idade: {self.obter_idade()} anos")

# Criar pessoa e testar
pessoa = Pessoa("João", 25)
pessoa.mostrar_info()
pessoa.definir_idade(30)
pessoa.mostrar_info()

Explicação linha por linha:

  • Linha 1: class Pessoa: - Define a classe Pessoa
  • Linha 2: def __init__(self, nome, idade): - Define o construtor
  • Linha 3: self.nome = nome - Atributo público
  • Linha 4: self.__idade = idade - Atributo privado (dois underscores)
  • Linha 6: def definir_idade(self, nova_idade): - Método para definir idade
  • Linhas 7-9: Verifica se idade é válida e define
  • Linhas 10-11: Trata idade inválida
  • Linha 13: def obter_idade(self): - Método para obter idade
  • Linha 14: return self.__idade - Retorna idade privada

14.0.0.1 Exercício 12: Classe com Múltiplos Métodos

Enunciado: Crie uma classe Retangulo com largura e altura. Crie métodos para calcular área, perímetro e verificar se é quadrado.

Resposta:

class Retangulo:
    def __init__(self, largura, altura):
        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)
    
    def eh_quadrado(self):
        return self.largura == self.altura
    
    def redimensionar(self, nova_largura, nova_altura):
        self.largura = nova_largura
        self.altura = nova_altura
    
    def mostrar_info(self):
        print(f"Largura: {self.largura}")
        print(f"Altura: {self.altura}")
        print(f"Área: {self.calcular_area()}")
        print(f"Perímetro: {self.calcular_perimetro()}")
        print(f"É quadrado: {self.eh_quadrado()}")

# Criar retângulo e testar
ret = Retangulo(5, 3)
ret.mostrar_info()
ret.redimensionar(4, 4)
ret.mostrar_info()

Explicação linha por linha:

  • Linha 1: class Retangulo: - Define a classe Retangulo
  • Linha 2: def __init__(self, largura, altura): - Define o construtor
  • Linhas 3-4: Define os atributos largura e altura
  • Linha 6: def calcular_area(self): - Método para calcular área
  • Linha 7: return self.largura * self.altura - Retorna área
  • Linha 9: def calcular_perimetro(self): - Método para calcular perímetro
  • Linha 10: return 2 * (self.largura + self.altura) - Retorna perímetro
  • Linha 12: def eh_quadrado(self): - Método para verificar se é quadrado
  • Linha 13: return self.largura == self.altura - Retorna True se for quadrado

14.0.0.2 Exercício 13: Classe com Validação

Enunciado: Crie uma classe ContaBancaria com número da conta e saldo. Adicione validações para todas as operações.

Resposta:

class ContaBancaria:
    def __init__(self, numero_conta, saldo_inicial=0):
        self.numero_conta = numero_conta
        self.saldo = saldo_inicial
        self.__limite_saque = 1000  # Limite privado
    
    def depositar(self, valor):
        if valor > 0:
            self.saldo += valor
            print(f"Depósito de R$ {valor:.2f} realizado!")
            return True
        else:
            print("Valor inválido para depósito!")
            return False
    
    def sacar(self, valor):
        if valor <= 0:
            print("Valor inválido para saque!")
            return False
        elif valor > self.saldo:
            print("Saldo insuficiente!")
            return False
        elif valor > self.__limite_saque:
            print(f"Valor excede o limite de R$ {self.__limite_saque:.2f}")
            return False
        else:
            self.saldo -= valor
            print(f"Saque de R$ {valor:.2f} realizado!")
            return True
    
    def transferir(self, outra_conta, valor):
        if self.sacar(valor):
            outra_conta.depositar(valor)
            print(f"Transferência de R$ {valor:.2f} realizada!")
            return True
        return False
    
    def mostrar_saldo(self):
        print(f"Saldo atual: R$ {self.saldo:.2f}")

# Criar contas e testar transferência
conta1 = ContaBancaria("12345", 1000)
conta2 = ContaBancaria("67890", 500)
conta1.transferir(conta2, 200)
conta1.mostrar_saldo()
conta2.mostrar_saldo()

Explicação linha por linha:

  • Linha 1: class ContaBancaria: - Define a classe ContaBancaria
  • Linha 2: def __init__(self, numero_conta, saldo_inicial=0): - Define construtor
  • Linhas 3-5: Define atributos incluindo limite privado
  • Linha 7: def depositar(self, valor): - Método para depositar
  • Linhas 8-12: Valida valor e deposita
  • Linhas 13-15: Trata valor inválido
  • Linha 17: def sacar(self, valor): - Método para sacar
  • Linhas 18-28: Validações múltiplas para saque
  • Linha 30: def transferir(self, outra_conta, valor): - Método para transferir
  • Linhas 31-35: Executa transferência entre contas

14.0.0.3 Exercício 14: Classe com Lista de Objetos

Enunciado: Crie uma classe Turma que gerencia uma lista de estudantes. Adicione métodos para adicionar, remover e listar estudantes.

Resposta:

class Estudante:
    def __init__(self, nome, idade, nota):
        self.nome = nome
        self.idade = idade
        self.nota = nota
    
    def mostrar_info(self):
        print(f"Nome: {self.nome}, Idade: {self.idade}, Nota: {self.nota}")

class Turma:
    def __init__(self, nome_turma):
        self.nome_turma = nome_turma
        self.estudantes = []
    
    def adicionar_estudante(self, estudante):
        self.estudantes.append(estudante)
        print(f"Estudante {estudante.nome} adicionado à turma!")
    
    def remover_estudante(self, nome):
        for estudante in self.estudantes:
            if estudante.nome == nome:
                self.estudantes.remove(estudante)
                print(f"Estudante {nome} removido da turma!")
                return True
        print(f"Estudante {nome} não encontrado!")
        return False
    
    def listar_estudantes(self):
        print(f"\n=== Turma: {self.nome_turma} ===")
        for estudante in self.estudantes:
            estudante.mostrar_info()
    
    def calcular_media_turma(self):
        if not self.estudantes:
            return 0
        total = sum(estudante.nota for estudante in self.estudantes)
        return total / len(self.estudantes)

# Criar turma e estudantes
turma = Turma("Python Básico")
est1 = Estudante("Ana", 20, 8.5)
est2 = Estudante("João", 19, 7.5)
turma.adicionar_estudante(est1)
turma.adicionar_estudante(est2)
turma.listar_estudantes()
print(f"Média da turma: {turma.calcular_media_turma():.2f}")

Explicação linha por linha:

  • Linha 1: class Estudante: - Define a classe Estudante
  • Linha 2: def __init__(self, nome, idade, nota): - Define construtor
  • Linhas 3-5: Define atributos do estudante
  • Linha 7: def mostrar_info(self): - Método para mostrar informações
  • Linha 8: print(f"Nome: {self.nome}, Idade: {self.idade}, Nota: {self.nota}") - Mostra info
  • Linha 10: class Turma: - Define a classe Turma
  • Linha 11: def __init__(self, nome_turma): - Define construtor
  • Linhas 12-13: Define atributos da turma
  • Linha 15: def adicionar_estudante(self, estudante): - Método para adicionar
  • Linhas 16-17: Adiciona estudante à lista
  • Linha 19: def remover_estudante(self, nome): - Método para remover
  • Linhas 20-26: Procura e remove estudante por nome

14.0.0.4 Exercício 15: Classe com Métodos Estáticos

Enunciado: Crie uma classe Matematica com métodos estáticos para operações matemáticas básicas.

Resposta:

class Matematica:
    @staticmethod
    def somar(a, b):
        return a + b
    
    @staticmethod
    def subtrair(a, b):
        return a - b
    
    @staticmethod
    def multiplicar(a, b):
        return a * b
    
    @staticmethod
    def dividir(a, b):
        if b == 0:
            return "Erro: divisão por zero!"
        return a / b
    
    @staticmethod
    def potencia(base, expoente):
        return base ** expoente
    
    @staticmethod
    def raiz_quadrada(numero):
        if numero < 0:
            return "Erro: raiz de número negativo!"
        return numero ** 0.5

# Usar métodos estáticos (não precisa criar objeto)
print(Matematica.somar(5, 3))  # 8
print(Matematica.potencia(2, 3))  # 8
print(Matematica.raiz_quadrada(16))  # 4.0
print(Matematica.dividir(10, 0))  # Erro

Explicação linha por linha:

  • Linha 1: class Matematica: - Define a classe Matematica
  • Linha 2: @staticmethod - Decorador para método estático
  • Linha 3: def somar(a, b): - Método estático para somar
  • Linha 4: return a + b - Retorna soma
  • Linha 6: @staticmethod - Decorador para método estático
  • Linha 7: def subtrair(a, b): - Método estático para subtrair
  • Linha 8: return a - b - Retorna subtração
  • Linha 10: @staticmethod - Decorador para método estático
  • Linha 11: def multiplicar(a, b): - Método estático para multiplicar
  • Linha 12: return a * b - Retorna multiplicação
  • Linha 14: @staticmethod - Decorador para método estático
  • Linha 15: def dividir(a, b): - Método estático para dividir
  • Linhas 16-18: Verifica divisão por zero
  • Linha 19: return a / b - Retorna divisão

14.0.1 Nível 4: Difícil (5 exercícios)

14.0.1.1 Exercício 16: Sistema de Herança - Animais

Enunciado: Crie uma classe Animal pai e classes filhas Cachorro e Gato que herdam dela.

Resposta:

class Animal:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
    
    def fazer_som(self):
        return "Algum som"
    
    def dormir(self):
        return f"{self.nome} está dormindo"
    
    def comer(self):
        return f"{self.nome} está comendo"

class Cachorro(Animal):
    def __init__(self, nome, idade, raca):
        super().__init__(nome, idade)  # Chama construtor da classe pai
        self.raca = raca
    
    def fazer_som(self):  # Sobrescreve método da classe pai
        return f"{self.nome} está latindo: Au au!"
    
    def buscar_osso(self):
        return f"{self.nome} está buscando um osso"

class Gato(Animal):
    def __init__(self, nome, idade, cor):
        super().__init__(nome, idade)  # Chama construtor da classe pai
        self.cor = cor
    
    def fazer_som(self):  # Sobrescreve método da classe pai
        return f"{self.nome} está miando: Miau!"
    
    def escalar(self):
        return f"{self.nome} está escalando"

# Criar animais e testar
cachorro = Cachorro("Rex", 3, "Golden Retriever")
gato = Gato("Mimi", 2, "Branco")

print(cachorro.fazer_som())  # Polimorfismo
print(gato.fazer_som())      # Polimorfismo
print(cachorro.buscar_osso())
print(gato.escalar())

Explicação linha por linha:

  • Linha 1: class Animal: - Define classe pai Animal
  • Linha 2: def __init__(self, nome, idade): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def fazer_som(self): - Método que será sobrescrito
  • Linha 7: return "Algum som" - Implementação genérica
  • Linha 9: def dormir(self): - Método herdado
  • Linha 10: return f"{self.nome} está dormindo" - Implementação
  • Linha 12: class Cachorro(Animal): - Classe filha que herda de Animal
  • Linha 13: def __init__(self, nome, idade, raca): - Construtor da classe filha
  • Linha 14: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 15: self.raca = raca - Atributo específico da classe filha
  • Linha 17: def fazer_som(self): - Sobrescreve método da classe pai
  • Linha 18: return f"{self.nome} está latindo: Au au!" - Implementação específica

14.0.1.2 Exercício 17: Sistema de Herança - Veículos

Enunciado: Crie uma classe Veiculo pai e classes filhas Carro e Moto que herdam dela.

Resposta:

class Veiculo:
    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"
    
    def mostrar_info(self):
        return f"{self.marca} {self.modelo} ({self.ano})"

class Carro(Veiculo):
    def __init__(self, marca, modelo, ano, portas):
        super().__init__(marca, modelo, ano)
        self.portas = portas
    
    def abrir_porta(self, numero_porta):
        return f"Porta {numero_porta} aberta"
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base} - {self.portas} portas"

class Moto(Veiculo):
    def __init__(self, marca, modelo, ano, cilindradas):
        super().__init__(marca, modelo, ano)
        self.cilindradas = cilindradas
    
    def empinar(self):
        return "Moto empinando!"
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base} - {self.cilindradas}cc"

# Criar veículos e testar
carro = Carro("Toyota", "Corolla", 2023, 4)
moto = Moto("Honda", "CB600", 2023, 600)

print(carro.mostrar_info())
print(moto.mostrar_info())
print(carro.acelerar(50))
print(moto.empinar())

Explicação linha por linha:

  • Linha 1: class Veiculo: - Define classe pai Veiculo
  • Linha 2: def __init__(self, marca, modelo, ano): - Construtor da classe pai
  • Linhas 3-6: Define atributos comuns
  • Linha 8: def acelerar(self, incremento): - Método para acelerar
  • Linhas 9-10: Aumenta velocidade
  • Linha 11: return f"Velocidade atual: {self.velocidade} km/h" - Retorna velocidade
  • Linha 13: def frear(self, decremento): - Método para frear
  • Linhas 14-15: Diminui velocidade (mínimo 0)
  • Linha 16: return f"Velocidade atual: {self.velocidade} km/h" - Retorna velocidade
  • Linha 18: def mostrar_info(self): - Método para mostrar informações
  • Linha 19: return f"{self.marca} {self.modelo} ({self.ano})" - Retorna info básica
  • Linha 21: class Carro(Veiculo): - Classe filha que herda de Veiculo
  • Linha 22: def __init__(self, marca, modelo, ano, portas): - Construtor da classe filha
  • Linha 23: super().__init__(marca, modelo, ano) - Chama construtor da classe pai
  • Linha 24: self.portas = portas - Atributo específico da classe filha
  • Linha 26: def abrir_porta(self, numero_porta): - Método específico da classe filha
  • Linha 27: return f"Porta {numero_porta} aberta" - Retorna mensagem
  • Linha 29: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 30-31: Chama método da classe pai e adiciona informações específicas

14.0.1.3 Exercício 18: Sistema de Herança - Funcionários

Enunciado: Crie uma classe Funcionario pai e classes filhas Gerente e Vendedor que herdam dela.

Resposta:

class Funcionario:
    def __init__(self, nome, salario_base):
        self.nome = nome
        self.salario_base = salario_base
    
    def calcular_salario(self):
        return self.salario_base
    
    def mostrar_info(self):
        return f"Nome: {self.nome}, Salário: R$ {self.calcular_salario():.2f}"

class Gerente(Funcionario):
    def __init__(self, nome, salario_base, bonus_gerencia):
        super().__init__(nome, salario_base)
        self.bonus_gerencia = bonus_gerencia
    
    def calcular_salario(self):  # Sobrescreve método da classe pai
        return self.salario_base + self.bonus_gerencia
    
    def gerenciar_equipe(self):
        return f"{self.nome} está gerenciando a equipe"

class Vendedor(Funcionario):
    def __init__(self, nome, salario_base, comissao):
        super().__init__(nome, salario_base)
        self.comissao = comissao
        self.vendas_mes = 0
    
    def calcular_salario(self):  # Sobrescreve método da classe pai
        return self.salario_base + (self.vendas_mes * self.comissao)
    
    def vender(self, valor):
        self.vendas_mes += valor
        return f"{self.nome} vendeu R$ {valor:.2f}"
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base}, Vendas do mês: R$ {self.vendas_mes:.2f}"

# Criar funcionários e testar
gerente = Gerente("Maria", 5000, 1000)
vendedor = Vendedor("João", 2000, 0.05)

print(gerente.mostrar_info())
print(vendedor.mostrar_info())
vendedor.vender(10000)
print(vendedor.mostrar_info())
print(gerente.gerenciar_equipe())

Explicação linha por linha:

  • Linha 1: class Funcionario: - Define classe pai Funcionario
  • Linha 2: def __init__(self, nome, salario_base): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def calcular_salario(self): - Método que será sobrescrito
  • Linha 7: return self.salario_base - Implementação básica
  • Linha 9: def mostrar_info(self): - Método para mostrar informações
  • Linha 10: return f"Nome: {self.nome}, Salário: R$ {self.calcular_salario():.2f}" - Retorna info
  • Linha 12: class Gerente(Funcionario): - Classe filha que herda de Funcionario
  • Linha 13: def __init__(self, nome, salario_base, bonus_gerencia): - Construtor da classe filha
  • Linha 14: super().__init__(nome, salario_base) - Chama construtor da classe pai
  • Linha 15: self.bonus_gerencia = bonus_gerencia - Atributo específico
  • Linha 17: def calcular_salario(self): - Sobrescreve método da classe pai
  • Linha 18: return self.salario_base + self.bonus_gerencia - Implementação específica
  • Linha 20: def gerenciar_equipe(self): - Método específico da classe filha
  • Linha 21: return f"{self.nome} está gerenciando a equipe" - Retorna mensagem
  • Linha 23: class Vendedor(Funcionario): - Classe filha que herda de Funcionario
  • Linha 24: def __init__(self, nome, salario_base, comissao): - Construtor da classe filha
  • Linha 25: super().__init__(nome, salario_base) - Chama construtor da classe pai
  • Linhas 26-27: Define atributos específicos
  • Linha 29: def calcular_salario(self): - Sobrescreve método da classe pai
  • Linha 30: return self.salario_base + (self.vendas_mes * self.comissao) - Implementação específica
  • Linha 32: def vender(self, valor): - Método específico da classe filha
  • Linhas 33-34: Registra venda e retorna mensagem
  • Linha 36: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 37-38: Chama método da classe pai e adiciona informações específicas

14.0.1.4 Exercício 19: Sistema de Herança - Formas Geométricas

Enunciado: Crie uma classe Forma pai e classes filhas Retangulo e Circulo que herdam dela.

Resposta:

import math

class Forma:
    def __init__(self, nome):
        self.nome = nome
    
    def calcular_area(self):
        return "Área não implementada"
    
    def calcular_perimetro(self):
        return "Perímetro não implementado"
    
    def mostrar_info(self):
        return f"Forma: {self.nome}"

class Retangulo(Forma):
    def __init__(self, largura, altura):
        super().__init__("Retângulo")
        self.largura = largura
        self.altura = altura
    
    def calcular_area(self):  # Sobrescreve método da classe pai
        return self.largura * self.altura
    
    def calcular_perimetro(self):  # Sobrescreve método da classe pai
        return 2 * (self.largura + self.altura)
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base}, Largura: {self.largura}, Altura: {self.altura}"

class Circulo(Forma):
    def __init__(self, raio):
        super().__init__("Círculo")
        self.raio = raio
    
    def calcular_area(self):  # Sobrescreve método da classe pai
        return math.pi * self.raio ** 2
    
    def calcular_perimetro(self):  # Sobrescreve método da classe pai
        return 2 * math.pi * self.raio
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base}, Raio: {self.raio}"

# Criar formas e testar polimorfismo
formas = [
    Retangulo(5, 3),
    Circulo(4),
    Retangulo(2, 8)
]

for forma in formas:
    print(f"{forma.mostrar_info()}")
    print(f"Área: {forma.calcular_area():.2f}")
    print(f"Perímetro: {forma.calcular_perimetro():.2f}")
    print("-" * 30)

Explicação linha por linha:

  • Linha 1: import math - Importa módulo math para usar π
  • Linha 3: class Forma: - Define classe pai Forma
  • Linha 4: def __init__(self, nome): - Construtor da classe pai
  • Linha 5: self.nome = nome - Define atributo nome
  • Linha 7: def calcular_area(self): - Método que será sobrescrito
  • Linha 8: return "Área não implementada" - Implementação genérica
  • Linha 10: def calcular_perimetro(self): - Método que será sobrescrito
  • Linha 11: return "Perímetro não implementado" - Implementação genérica
  • Linha 13: def mostrar_info(self): - Método para mostrar informações
  • Linha 14: return f"Forma: {self.nome}" - Retorna nome da forma
  • Linha 16: class Retangulo(Forma): - Classe filha que herda de Forma
  • Linha 17: def __init__(self, largura, altura): - Construtor da classe filha
  • Linha 18: super().__init__("Retângulo") - Chama construtor da classe pai
  • Linhas 19-20: Define atributos específicos
  • Linha 22: def calcular_area(self): - Sobrescreve método da classe pai
  • Linha 23: return self.largura * self.altura - Implementação específica
  • Linha 25: def calcular_perimetro(self): - Sobrescreve método da classe pai
  • Linha 26: return 2 * (self.largura + self.altura) - Implementação específica
  • Linha 28: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 29-30: Chama método da classe pai e adiciona informações específicas
  • Linha 32: class Circulo(Forma): - Classe filha que herda de Forma
  • Linha 33: def __init__(self, raio): - Construtor da classe filha
  • Linha 34: super().__init__("Círculo") - Chama construtor da classe pai
  • Linha 35: self.raio = raio - Define atributo específico
  • Linha 37: def calcular_area(self): - Sobrescreve método da classe pai
  • Linha 38: return math.pi * self.raio ** 2 - Implementação específica
  • Linha 40: def calcular_perimetro(self): - Sobrescreve método da classe pai
  • Linha 41: return 2 * math.pi * self.raio - Implementação específica
  • Linha 43: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 44-45: Chama método da classe pai e adiciona informações específicas
  • Linha 47: formas = [...] - Lista com objetos de diferentes classes
  • Linha 51: for forma in formas: - Loop para testar polimorfismo
  • Linhas 52-55: Chama métodos que se comportam diferente para cada tipo

14.0.1.5 Exercício 20: Sistema de Herança - Contas Bancárias

Enunciado: Crie uma classe Conta pai e classes filhas ContaCorrente e ContaPoupanca que herdam dela.

Resposta:

class Conta:
    def __init__(self, numero_conta, saldo_inicial=0):
        self.numero_conta = numero_conta
        self.saldo = saldo_inicial
    
    def depositar(self, valor):
        if valor > 0:
            self.saldo += valor
            return f"Depósito de R$ {valor:.2f} realizado!"
        return "Valor inválido para depósito!"
    
    def sacar(self, valor):
        if valor > 0 and valor <= self.saldo:
            self.saldo -= valor
            return f"Saque de R$ {valor:.2f} realizado!"
        return "Saldo insuficiente ou valor inválido!"
    
    def mostrar_saldo(self):
        return f"Saldo atual: R$ {self.saldo:.2f}"

class ContaCorrente(Conta):
    def __init__(self, numero_conta, saldo_inicial=0, limite=1000):
        super().__init__(numero_conta, saldo_inicial)
        self.limite = limite
    
    def sacar(self, valor):  # Sobrescreve método da classe pai
        if valor > 0 and valor <= (self.saldo + self.limite):
            self.saldo -= valor
            return f"Saque de R$ {valor:.2f} realizado!"
        return "Saldo insuficiente ou valor inválido!"
    
    def usar_cheque(self, valor):
        if valor > 0 and valor <= (self.saldo + self.limite):
            self.saldo -= valor
            return f"Cheque de R$ {valor:.2f} usado!"
        return "Saldo insuficiente para usar cheque!"

class ContaPoupanca(Conta):
    def __init__(self, numero_conta, saldo_inicial=0, taxa_juros=0.01):
        super().__init__(numero_conta, saldo_inicial)
        self.taxa_juros = taxa_juros
    
    def aplicar_juros(self):
        juros = self.saldo * self.taxa_juros
        self.saldo += juros
        return f"Juros de R$ {juros:.2f} aplicados!"
    
    def sacar(self, valor):  # Sobrescreve método da classe pai
        if valor > 0 and valor <= self.saldo:
            self.saldo -= valor
            return f"Saque de R$ {valor:.2f} realizado!"
        return "Saldo insuficiente ou valor inválido!"

# Criar contas e testar
conta_corrente = ContaCorrente("12345", 1000, 500)
conta_poupanca = ContaPoupanca("67890", 2000, 0.02)

print(conta_corrente.sacar(1200))  # Usa limite
print(conta_corrente.usar_cheque(300))
print(conta_poupanca.aplicar_juros())
print(conta_poupanca.mostrar_saldo())

Explicação linha por linha:

  • Linha 1: class Conta: - Define classe pai Conta
  • Linha 2: def __init__(self, numero_conta, saldo_inicial=0): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def depositar(self, valor): - Método para depositar
  • Linhas 7-10: Valida valor e deposita
  • Linha 11: return "Valor inválido para depósito!" - Trata valor inválido
  • Linha 13: def sacar(self, valor): - Método que será sobrescrito
  • Linhas 14-17: Valida valor e saldo, saca
  • Linha 18: return "Saldo insuficiente ou valor inválido!" - Trata erro
  • Linha 20: def mostrar_saldo(self): - Método para mostrar saldo
  • Linha 21: return f"Saldo atual: R$ {self.saldo:.2f}" - Retorna saldo
  • Linha 23: class ContaCorrente(Conta): - Classe filha que herda de Conta
  • Linha 24: def __init__(self, numero_conta, saldo_inicial=0, limite=1000): - Construtor da classe filha
  • Linha 25: super().__init__(numero_conta, saldo_inicial) - Chama construtor da classe pai
  • Linha 26: self.limite = limite - Define atributo específico
  • Linha 28: def sacar(self, valor): - Sobrescreve método da classe pai
  • Linhas 29-32: Implementação específica que considera limite
  • Linha 33: return "Saldo insuficiente ou valor inválido!" - Trata erro
  • Linha 35: def usar_cheque(self, valor): - Método específico da classe filha
  • Linhas 36-39: Implementação específica para usar cheque
  • Linha 40: return "Saldo insuficiente para usar cheque!" - Trata erro
  • Linha 42: class ContaPoupanca(Conta): - Classe filha que herda de Conta
  • Linha 43: def __init__(self, numero_conta, saldo_inicial=0, taxa_juros=0.01): - Construtor da classe filha
  • Linha 44: super().__init__(numero_conta, saldo_inicial) - Chama construtor da classe pai
  • Linha 45: self.taxa_juros = taxa_juros - Define atributo específico
  • Linha 47: def aplicar_juros(self): - Método específico da classe filha
  • Linhas 48-50: Calcula e aplica juros
  • Linha 51: return f"Juros de R$ {juros:.2f} aplicados!" - Retorna mensagem
  • Linha 53: def sacar(self, valor): - Sobrescreve método da classe pai
  • Linhas 54-57: Implementação específica que não permite saldo negativo
  • Linha 58: return "Saldo insuficiente ou valor inválido!" - Trata erro

14.0.2 Nível 5: Muito Difícil (5 exercícios)

14.0.2.1 Exercício 21: Sistema Completo de Biblioteca

Enunciado: Crie um sistema completo de biblioteca com classes Livro, Usuario e Biblioteca que gerencia empréstimos.

Resposta:

from datetime import datetime, timedelta

class Livro:
    def __init__(self, titulo, autor, isbn):
        self.titulo = titulo
        self.autor = autor
        self.isbn = isbn
        self.disponivel = True
        self.data_emprestimo = None
        self.usuario_emprestimo = None
    
    def emprestar(self, usuario):
        if self.disponivel:
            self.disponivel = False
            self.data_emprestimo = datetime.now()
            self.usuario_emprestimo = usuario
            return f"Livro '{self.titulo}' emprestado para {usuario.nome}"
        return f"Livro '{self.titulo}' não está disponível"
    
    def devolver(self):
        if not self.disponivel:
            self.disponivel = True
            self.data_emprestimo = None
            self.usuario_emprestimo = None
            return f"Livro '{self.titulo}' devolvido"
        return f"Livro '{self.titulo}' já está disponível"
    
    def mostrar_info(self):
        status = "Disponível" if self.disponivel else f"Emprestado para {self.usuario_emprestimo.nome}"
        return f"Título: {self.titulo}, Autor: {self.autor}, Status: {status}"

class Usuario:
    def __init__(self, nome, email):
        self.nome = nome
        self.email = email
        self.livros_emprestados = []
    
    def emprestar_livro(self, livro):
        if len(self.livros_emprestados) < 3:  # Limite de 3 livros
            resultado = livro.emprestar(self)
            if "emprestado" in resultado:
                self.livros_emprestados.append(livro)
            return resultado
        return "Limite de empréstimos atingido (máximo 3 livros)"
    
    def devolver_livro(self, livro):
        if livro in self.livros_emprestados:
            resultado = livro.devolver()
            if "devolvido" in resultado:
                self.livros_emprestados.remove(livro)
            return resultado
        return f"Você não tem o livro '{livro.titulo}' emprestado"
    
    def mostrar_livros_emprestados(self):
        if self.livros_emprestados:
            print(f"Livros emprestados por {self.nome}:")
            for livro in self.livros_emprestados:
                print(f"- {livro.titulo}")
        else:
            print(f"{self.nome} não tem livros emprestados")

class Biblioteca:
    def __init__(self, nome):
        self.nome = nome
        self.livros = []
        self.usuarios = []
    
    def adicionar_livro(self, livro):
        self.livros.append(livro)
        return f"Livro '{livro.titulo}' adicionado à biblioteca"
    
    def adicionar_usuario(self, usuario):
        self.usuarios.append(usuario)
        return f"Usuário {usuario.nome} cadastrado na biblioteca"
    
    def buscar_livro(self, titulo):
        for livro in self.livros:
            if titulo.lower() in livro.titulo.lower():
                return livro
        return None
    
    def listar_livros_disponiveis(self):
        disponiveis = [livro for livro in self.livros if livro.disponivel]
        if disponiveis:
            print("Livros disponíveis:")
            for livro in disponiveis:
                print(f"- {livro.titulo} por {livro.autor}")
        else:
            print("Nenhum livro disponível")
    
    def relatorio_geral(self):
        print(f"\n=== Relatório da Biblioteca {self.nome} ===")
        print(f"Total de livros: {len(self.livros)}")
        print(f"Total de usuários: {len(self.usuarios)}")
        disponiveis = len([livro for livro in self.livros if livro.disponivel])
        print(f"Livros disponíveis: {disponiveis}")
        print(f"Livros emprestados: {len(self.livros) - disponiveis}")

# Criar sistema de biblioteca
biblioteca = Biblioteca("Biblioteca Central")

# Criar livros
livro1 = Livro("Python para Iniciantes", "João Silva", "123456")
livro2 = Livro("Algoritmos e Estruturas de Dados", "Maria Santos", "789012")
livro3 = Livro("Machine Learning", "Pedro Costa", "345678")

# Adicionar livros à biblioteca
biblioteca.adicionar_livro(livro1)
biblioteca.adicionar_livro(livro2)
biblioteca.adicionar_livro(livro3)

# Criar usuários
usuario1 = Usuario("Ana", "ana@email.com")
usuario2 = Usuario("Carlos", "carlos@email.com")

# Cadastrar usuários
biblioteca.adicionar_usuario(usuario1)
biblioteca.adicionar_usuario(usuario2)

# Testar empréstimos
print(usuario1.emprestar_livro(livro1))
print(usuario1.emprestar_livro(livro2))
print(usuario2.emprestar_livro(livro3))

# Mostrar relatório
biblioteca.relatorio_geral()
usuario1.mostrar_livros_emprestados()

Explicação linha por linha:

  • Linha 1: from datetime import datetime, timedelta - Importa módulos para datas
  • Linha 3: class Livro: - Define classe Livro
  • Linha 4: def __init__(self, titulo, autor, isbn): - Construtor da classe Livro
  • Linhas 5-8: Define atributos do livro
  • Linha 9: self.disponivel = True - Inicializa como disponível
  • Linha 10: self.data_emprestimo = None - Inicializa data de empréstimo
  • Linha 11: self.usuario_emprestimo = None - Inicializa usuário do empréstimo
  • Linha 13: def emprestar(self, usuario): - Método para emprestar livro
  • Linhas 14-18: Verifica disponibilidade e empresta
  • Linha 19: return f"Livro '{self.titulo}' emprestado para {usuario.nome}" - Retorna sucesso
  • Linha 20: return f"Livro '{self.titiltulo}' não está disponível" - Retorna erro
  • Linha 22: def devolver(self): - Método para devolver livro
  • Linhas 23-27: Verifica se está emprestado e devolve
  • Linha 28: return f"Livro '{self.titulo}' devolvido" - Retorna sucesso
  • Linha 29: return f"Livro '{self.titulo}' já está disponível" - Retorna erro
  • Linha 31: def mostrar_info(self): - Método para mostrar informações
  • Linhas 32-34: Mostra informações do livro
  • Linha 36: class Usuario: - Define classe Usuario
  • Linha 37: def __init__(self, nome, email): - Construtor da classe Usuario
  • Linhas 38-40: Define atributos do usuário
  • Linha 41: self.livros_emprestados = [] - Lista de livros emprestados
  • Linha 43: def emprestar_livro(self, livro): - Método para emprestar livro
  • Linhas 44-49: Verifica limite e empresta livro
  • Linha 50: return "Limite de empréstimos atingido (máximo 3 livros)" - Retorna erro
  • Linha 52: def devolver_livro(self, livro): - Método para devolver livro
  • Linhas 53-58: Verifica se tem o livro e devolve
  • Linha 59: return f"Você não tem o livro '{livro.titulo}' emprestado" - Retorna erro
  • Linha 61: def mostrar_livros_emprestados(self): - Método para mostrar livros emprestados
  • Linhas 62-67: Mostra lista de livros emprestados
  • Linha 69: class Biblioteca: - Define classe Biblioteca
  • Linha 70: def __init__(self, nome): - Construtor da classe Biblioteca
  • Linhas 71-74: Define atributos da biblioteca
  • Linha 75: self.livros = [] - Lista de livros
  • Linha 76: self.usuarios = [] - Lista de usuários
  • Linha 78: def adicionar_livro(self, livro): - Método para adicionar livro
  • Linhas 79-80: Adiciona livro à lista
  • Linha 81: return f"Livro '{livro.titulo}' adicionado à biblioteca" - Retorna sucesso
  • Linha 83: def adicionar_usuario(self, usuario): - Método para adicionar usuário
  • Linhas 84-85: Adiciona usuário à lista
  • Linha 86: return f"Usuário {usuario.nome} cadastrado na biblioteca" - Retorna sucesso
  • Linha 88: def buscar_livro(self, titulo): - Método para buscar livro
  • Linhas 89-92: Procura livro por título
  • Linha 93: return None - Retorna None se não encontrar
  • Linha 95: def listar_livros_disponiveis(self): - Método para listar livros disponíveis
  • Linhas 96-103: Lista livros disponíveis
  • Linha 105: def relatorio_geral(self): - Método para relatório geral
  • Linhas 106-112: Mostra relatório da biblioteca

14.0.2.2 Exercício 22: Sistema de Herança Múltipla

Enunciado: Crie um sistema com herança múltipla: PessoaFuncionarioGerente e PessoaClienteClienteVip.

Resposta:

class Pessoa:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
    
    def apresentar(self):
        return f"Olá, eu sou {self.nome} e tenho {self.idade} anos"

class Funcionario(Pessoa):
    def __init__(self, nome, idade, salario):
        super().__init__(nome, idade)
        self.salario = salario
    
    def calcular_salario(self):
        return self.salario
    
    def trabalhar(self):
        return f"{self.nome} está trabalhando"

class Gerente(Funcionario):
    def __init__(self, nome, idade, salario, bonus_gerencia):
        super().__init__(nome, idade, salario)
        self.bonus_gerencia = bonus_gerencia
    
    def calcular_salario(self):
        return self.salario + self.bonus_gerencia
    
    def gerenciar_equipe(self):
        return f"{self.nome} está gerenciando a equipe"

class Cliente(Pessoa):
    def __init__(self, nome, idade, email):
        super().__init__(nome, idade)
        self.email = email
        self.compras = []
    
    def comprar(self, produto):
        self.compras.append(produto)
        return f"{self.nome} comprou {produto}"
    
    def mostrar_compras(self):
        if self.compras:
            return f"Compras de {self.nome}: {', '.join(self.compras)}"
        return f"{self.nome} não fez compras ainda"

class ClienteVip(Cliente):
    def __init__(self, nome, idade, email, desconto):
        super().__init__(nome, idade, email)
        self.desconto = desconto
    
    def comprar(self, produto):
        self.compras.append(f"{produto} (com desconto de {self.desconto}%)")
        return f"{self.nome} comprou {produto} com desconto de {self.desconto}%"
    
    def mostrar_desconto(self):
        return f"{self.nome} tem desconto de {self.desconto}%"

# Criar objetos e testar
gerente = Gerente("Maria", 35, 5000, 1000)
cliente_vip = ClienteVip("João", 28, "joao@email.com", 15)

print(gerente.apresentar())
print(gerente.calcular_salario())
print(gerente.gerenciar_equipe())
print(cliente_vip.apresentar())
print(cliente_vip.comprar("Notebook"))
print(cliente_vip.mostrar_desconto())

Explicação linha por linha:

  • Linha 1: class Pessoa: - Define classe pai Pessoa
  • Linha 2: def __init__(self, nome, idade): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def apresentar(self): - Método para apresentar
  • Linha 7: return f"Olá, eu sou {self.nome} e tenho {self.idade} anos" - Retorna apresentação
  • Linha 9: class Funcionario(Pessoa): - Classe filha que herda de Pessoa
  • Linha 10: def __init__(self, nome, idade, salario): - Construtor da classe filha
  • Linha 11: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 12: self.salario = salario - Define atributo específico
  • Linha 14: def calcular_salario(self): - Método para calcular salário
  • Linha 15: return self.salario - Retorna salário
  • Linha 17: def trabalhar(self): - Método para trabalhar
  • Linha 18: return f"{self.nome} está trabalhando" - Retorna mensagem
  • Linha 20: class Gerente(Funcionario): - Classe filha que herda de Funcionario
  • Linha 21: def __init__(self, nome, idade, salario, bonus_gerencia): - Construtor da classe filha
  • Linha 22: super().__init__(nome, idade, salario) - Chama construtor da classe pai
  • Linha 23: self.bonus_gerencia = bonus_gerencia - Define atributo específico
  • Linha 25: def calcular_salario(self): - Sobrescreve método da classe pai
  • Linha 26: return self.salario + self.bonus_gerencia - Implementação específica
  • Linha 28: def gerenciar_equipe(self): - Método específico da classe filha
  • Linha 29: return f"{self.nome} está gerenciando a equipe" - Retorna mensagem
  • Linha 31: class Cliente(Pessoa): - Classe filha que herda de Pessoa
  • Linha 32: def __init__(self, nome, idade, email): - Construtor da classe filha
  • Linha 33: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linhas 34-35: Define atributos específicos
  • Linha 36: self.compras = [] - Lista de compras
  • Linha 38: def comprar(self, produto): - Método para comprar
  • Linhas 39-40: Adiciona produto à lista de compras
  • Linha 41: return f"{self.nome} comprou {produto}" - Retorna mensagem
  • Linha 43: def mostrar_compras(self): - Método para mostrar compras
  • Linhas 44-46: Mostra lista de compras
  • Linha 48: class ClienteVip(Cliente): - Classe filha que herda de Cliente
  • Linha 49: def __init__(self, nome, idade, email, desconto): - Construtor da classe filha
  • Linha 50: super().__init__(nome, idade, email) - Chama construtor da classe pai
  • Linha 51: self.desconto = desconto - Define atributo específico
  • Linha 53: def comprar(self, produto): - Sobrescreve método da classe pai
  • Linhas 54-55: Implementação específica com desconto
  • Linha 56: return f"{self.nome} comprou {produto} com desconto de {self.desconto}%" - Retorna mensagem
  • Linha 58: def mostrar_desconto(self): - Método específico da classe filha
  • Linha 59: return f"{self.nome} tem desconto de {self.desconto}%" - Retorna mensagem

14.0.2.3 Exercício 23: Sistema de Herança com Polimorfismo

Enunciado: Crie um sistema com classes Animal, Cachorro, Gato e Galinha que demonstrem polimorfismo.

Resposta:

class Animal:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
    
    def fazer_som(self):
        return "Algum som"
    
    def mover(self):
        return "Movendo"
    
    def dormir(self):
        return f"{self.nome} está dormindo"
    
    def mostrar_info(self):
        return f"Nome: {self.nome}, Idade: {self.idade} anos"

class Cachorro(Animal):
    def __init__(self, nome, idade, raca):
        super().__init__(nome, idade)
        self.raca = raca
    
    def fazer_som(self):  # Polimorfismo
        return f"{self.nome} está latindo: Au au!"
    
    def mover(self):  # Polimorfismo
        return f"{self.nome} está correndo"
    
    def buscar_osso(self):
        return f"{self.nome} está buscando um osso"

class Gato(Animal):
    def __init__(self, nome, idade, cor):
        super().__init__(nome, idade)
        self.cor = cor
    
    def fazer_som(self):  # Polimorfismo
        return f"{self.nome} está miando: Miau!"
    
    def mover(self):  # Polimorfismo
        return f"{self.nome} está caminhando silenciosamente"
    
    def escalar(self):
        return f"{self.nome} está escalando"

class Galinha(Animal):
    def __init__(self, nome, idade, cor_penas):
        super().__init__(nome, idade)
        self.cor_penas = cor_penas
    
    def fazer_som(self):  # Polimorfismo
        return f"{self.nome} está cacarejando: Cocoricó!"
    
    def mover(self):  # Polimorfismo
        return f"{self.nome} está caminhando"
    
    def botar_ovo(self):
        return f"{self.nome} botou um ovo"

# Criar lista de animais (polimorfismo)
animais = [
    Cachorro("Rex", 3, "Golden Retriever"),
    Gato("Mimi", 2, "Branco"),
    Galinha("Penélope", 1, "Marrom")
]

# Testar polimorfismo
for animal in animais:
    print(f"{animal.mostrar_info()}")
    print(f"Som: {animal.fazer_som()}")
    print(f"Movimento: {animal.mover()}")
    print(f"Sono: {animal.dormir()}")
    print("-" * 40)

# Testar métodos específicos
cachorro = animais[0]
gato = animais[1]
galinha = animais[2]

print(cachorro.buscar_osso())
print(gato.escalar())
print(galinha.botar_ovo())

Explicação linha por linha:

  • Linha 1: class Animal: - Define classe pai Animal
  • Linha 2: def __init__(self, nome, idade): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def fazer_som(self): - Método que será sobrescrito
  • Linha 7: return "Algum som" - Implementação genérica
  • Linha 9: def mover(self): - Método que será sobrescrito
  • Linha 10: return "Movendo" - Implementação genérica
  • Linha 12: def dormir(self): - Método herdado
  • Linha 13: return f"{self.nome} está dormindo" - Implementação
  • Linha 15: def mostrar_info(self): - Método para mostrar informações
  • Linha 16: return f"Nome: {self.nome}, Idade: {self.idade} anos" - Retorna informações
  • Linha 18: class Cachorro(Animal): - Classe filha que herda de Animal
  • Linha 19: def __init__(self, nome, idade, raca): - Construtor da classe filha
  • Linha 20: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 21: self.raca = raca - Define atributo específico
  • Linha 23: def fazer_som(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 24: return f"{self.nome} está latindo: Au au!" - Implementação específica
  • Linha 26: def mover(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 27: return f"{self.nome} está correndo" - Implementação específica
  • Linha 29: def buscar_osso(self): - Método específico da classe filha
  • Linha 30: return f"{self.nome} está buscando um osso" - Retorna mensagem
  • Linha 32: class Gato(Animal): - Classe filha que herda de Animal
  • Linha 33: def __init__(self, nome, idade, cor): - Construtor da classe filha
  • Linha 34: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 35: self.cor = cor - Define atributo específico
  • Linha 37: def fazer_som(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 38: return f"{self.nome} está miando: Miau!" - Implementação específica
  • Linha 40: def mover(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 41: return f"{self.nome} está caminhando silenciosamente" - Implementação específica
  • Linha 43: def escalar(self): - Método específico da classe filha
  • Linha 44: return f"{self.nome} está escalando" - Retorna mensagem
  • Linha 46: class Galinha(Animal): - Classe filha que herda de Animal
  • Linha 47: def __init__(self, nome, idade, cor_penas): - Construtor da classe filha
  • Linha 48: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 49: self.cor_penas = cor_penas - Define atributo específico
  • Linha 51: def fazer_som(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 52: return f"{self.nome} está cacarejando: Cocoricó!" - Implementação específica
  • Linha 54: def mover(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 55: return f"{self.nome} está caminhando" - Implementação específica
  • Linha 57: def botar_ovo(self): - Método específico da classe filha
  • Linha 58: return f"{self.nome} botou um ovo" - Retorna mensagem
  • Linha 60: animais = [...] - Lista com objetos de diferentes classes
  • Linha 64: for animal in animais: - Loop para testar polimorfismo
  • Linhas 65-69: Chama métodos que se comportam diferente para cada tipo

14.0.2.4 Exercício 24: Sistema de Herança com Métodos Abstratos

Enunciado: Crie um sistema com classe abstrata Forma e classes filhas Retangulo e Circulo.

Resposta:

from abc import ABC, abstractmethod
import math

class Forma(ABC):
    def __init__(self, nome):
        self.nome = nome
    
    @abstractmethod
    def calcular_area(self):
        pass
    
    @abstractmethod
    def calcular_perimetro(self):
        pass
    
    def mostrar_info(self):
        return f"Forma: {self.nome}"

class Retangulo(Forma):
    def __init__(self, largura, altura):
        super().__init__("Retângulo")
        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)
    
    def mostrar_info(self):
        info_base = super().mostrar_info()
        return f"{info_base}, Largura: {self.largura}, Altura: {self.altura}"

class Circulo(Forma):
    def __init__(self, raio):
        super().__init__("Círculo")
        self.raio = raio
    
    def calcular_area(self):
        return math.pi * self.raio ** 2
    
    def calcular_perimetro(self):
        return 2 * math.pi * self.raio
    
    def mostrar_info(self):
        info_base = super().mostrar_info()
        return f"{info_base}, Raio: {self.raio}"

# Criar formas e testar
formas = [
    Retangulo(5, 3),
    Circulo(4),
    Retangulo(2, 8)
]

for forma in formas:
    print(f"{forma.mostrar_info()}")
    print(f"Área: {forma.calcular_area():.2f}")
    print(f"Perímetro: {forma.calcular_perimetro():.2f}")
    print("-" * 30)

Explicação linha por linha:

  • Linha 1: from abc import ABC, abstractmethod - Importa módulos para classes abstratas
  • Linha 2: import math - Importa módulo math para usar π
  • Linha 4: class Forma(ABC): - Define classe abstrata Forma
  • Linha 5: def __init__(self, nome): - Construtor da classe abstrata
  • Linha 6: self.nome = nome - Define atributo nome
  • Linha 8: @abstractmethod - Decorador para método abstrato
  • Linha 9: def calcular_area(self): - Método abstrato que deve ser implementado
  • Linha 10: pass - Placeholder para método abstrato
  • Linha 12: @abstractmethod - Decorador para método abstrato
  • Linha 13: def calcular_perimetro(self): - Método abstrato que deve ser implementado
  • Linha 14: pass - Placeholder para método abstrato
  • Linha 16: def mostrar_info(self): - Método concreto da classe abstrata
  • Linha 17: return f"Forma: {self.nome}" - Retorna informações básicas
  • Linha 19: class Retangulo(Forma): - Classe filha que herda de Forma
  • Linha 20: def __init__(self, largura, altura): - Construtor da classe filha
  • Linha 21: super().__init__("Retângulo") - Chama construtor da classe pai
  • Linhas 22-23: Define atributos específicos
  • Linha 25: def calcular_area(self): - Implementa método abstrato
  • Linha 26: return self.largura * self.altura - Implementação específica
  • Linha 28: def calcular_perimetro(self): - Implementa método abstrato
  • Linha 29: return 2 * (self.largura + self.altura) - Implementação específica
  • Linha 31: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 32-33: Chama método da classe pai e adiciona informações específicas
  • Linha 35: class Circulo(Forma): - Classe filha que herda de Forma
  • Linha 36: def __init__(self, raio): - Construtor da classe filha
  • Linha 37: super().__init__("Círculo") - Chama construtor da classe pai
  • Linha 38: self.raio = raio - Define atributo específico
  • Linha 40: def calcular_area(self): - Implementa método abstrato
  • Linha 41: return math.pi * self.raio ** 2 - Implementação específica
  • Linha 43: def calcular_perimetro(self): - Implementa método abstrato
  • Linha 44: return 2 * math.pi * self.raio - Implementação específica
  • Linha 46: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 47-48: Chama método da classe pai e adiciona informações específicas
  • Linha 50: formas = [...] - Lista com objetos de diferentes classes
  • Linha 54: for forma in formas: - Loop para testar polimorfismo
  • Linhas 55-58: Chama métodos que se comportam diferente para cada tipo

14.0.2.5 Exercício 25: Sistema Completo de POO

Enunciado: Crie um sistema completo de gerenciamento de uma loja com classes Produto, Cliente, Funcionario e Loja.

Resposta:

class Produto:
    def __init__(self, nome, preco, estoque):
        self.nome = nome
        self.preco = preco
        self.estoque = estoque
    
    def vender(self, quantidade):
        if quantidade <= self.estoque:
            self.estoque -= quantidade
            return f"Vendidos {quantidade} {self.nome}(s)"
        return f"Estoque insuficiente de {self.nome}"
    
    def repor_estoque(self, quantidade):
        self.estoque += quantidade
        return f"Repostos {quantidade} {self.nome}(s)"
    
    def mostrar_info(self):
        return f"Produto: {self.nome}, Preço: R$ {self.preco:.2f}, Estoque: {self.estoque}"

class Cliente:
    def __init__(self, nome, email):
        self.nome = nome
        self.email = email
        self.compras = []
    
    def comprar(self, produto, quantidade):
        resultado = produto.vender(quantidade)
        if "Vendidos" in resultado:
            self.compras.append(f"{quantidade} {produto.nome}")
        return resultado
    
    def mostrar_compras(self):
        if self.compras:
            return f"Compras de {self.nome}: {', '.join(self.compras)}"
        return f"{self.nome} não fez compras ainda"

class Funcionario:
    def __init__(self, nome, salario):
        self.nome = nome
        self.salario = salario
        self.vendas = 0
    
    def vender(self, produto, quantidade):
        resultado = produto.vender(quantidade)
        if "Vendidos" in resultado:
            self.vendas += quantidade
        return resultado
    
    def calcular_comissao(self, taxa=0.05):
        return self.vendas * taxa
    
    def mostrar_info(self):
        return f"Funcionário: {self.nome}, Salário: R$ {self.salario:.2f}, Vendas: {self.vendas}"

class Loja:
    def __init__(self, nome):
        self.nome = nome
        self.produtos = []
        self.clientes = []
        self.funcionarios = []
    
    def adicionar_produto(self, produto):
        self.produtos.append(produto)
        return f"Produto {produto.nome} adicionado à loja"
    
    def adicionar_cliente(self, cliente):
        self.clientes.append(cliente)
        return f"Cliente {cliente.nome} cadastrado"
    
    def adicionar_funcionario(self, funcionario):
        self.funcionarios.append(funcionario)
        return f"Funcionário {funcionario.nome} contratado"
    
    def buscar_produto(self, nome):
        for produto in self.produtos:
            if nome.lower() in produto.nome.lower():
                return produto
        return None
    
    def relatorio_estoque(self):
        print(f"\n=== Relatório de Estoque - {self.nome} ===")
        for produto in self.produtos:
            print(produto.mostrar_info())
    
    def relatorio_funcionarios(self):
        print(f"\n=== Relatório de Funcionários - {self.nome} ===")
        for funcionario in self.funcionarios:
            print(funcionario.mostrar_info())
            print(f"Comissão: R$ {funcionario.calcular_comissao():.2f}")

# Criar sistema de loja
loja = Loja("Loja Python")

# Criar produtos
produto1 = Produto("Notebook", 2000, 10)
produto2 = Produto("Mouse", 50, 50)
produto3 = Produto("Teclado", 100, 30)

# Adicionar produtos à loja
loja.adicionar_produto(produto1)
loja.adicionar_produto(produto2)
loja.adicionar_produto(produto3)

# Criar clientes
cliente1 = Cliente("Ana", "ana@email.com")
cliente2 = Cliente("João", "joao@email.com")

# Cadastrar clientes
loja.adicionar_cliente(cliente1)
loja.adicionar_cliente(cliente2)

# Criar funcionários
funcionario1 = Funcionario("Maria", 3000)
funcionario2 = Funcionario("Pedro", 2500)

# Contratar funcionários
loja.adicionar_funcionario(funcionario1)
loja.adicionar_funcionario(funcionario2)

# Testar vendas
print(funcionario1.vender(produto1, 2))
print(funcionario2.vender(produto2, 5))
print(cliente1.comprar(produto3, 1))

# Mostrar relatórios
loja.relatorio_estoque()
loja.relatorio_funcionarios()

Explicação linha por linha:

  • Linha 1: class Produto: - Define classe Produto
  • Linha 2: def __init__(self, nome, preco, estoque): - Construtor da classe Produto
  • Linhas 3-5: Define atributos do produto
  • Linha 7: def vender(self, quantidade): - Método para vender produto
  • Linhas 8-11: Verifica estoque e vende
  • Linha 12: return f"Estoque insuficiente de {self.nome}" - Retorna erro
  • Linha 14: def repor_estoque(self, quantidade): - Método para repor estoque
  • Linhas 15-16: Adiciona quantidade ao estoque
  • Linha 17: return f"Repostos {quantidade} {self.nome}(s)" - Retorna sucesso
  • Linha 19: def mostrar_info(self): - Método para mostrar informações
  • Linha 20: return f"Produto: {self.nome}, Preço: R$ {self.preco:.2f}, Estoque: {self.estoque}" - Retorna informações
  • Linha 22: class Cliente: - Define classe Cliente
  • Linha 23: def __init__(self, nome, email): - Construtor da classe Cliente
  • Linhas 24-26: Define atributos do cliente
  • Linha 27: self.compras = [] - Lista de compras
  • Linha 29: def comprar(self, produto, quantidade): - Método para comprar
  • Linhas 30-33: Executa compra e registra
  • Linha 34: return resultado - Retorna resultado da compra
  • Linha 36: def mostrar_compras(self): - Método para mostrar compras
  • Linhas 37-40: Mostra lista de compras
  • Linha 42: class Funcionario: - Define classe Funcionario
  • Linha 43: def __init__(self, nome, salario): - Construtor da classe Funcionario
  • Linhas 44-46: Define atributos do funcionário
  • Linha 47: self.vendas = 0 - Contador de vendas
  • Linha 49: def vender(self, produto, quantidade): - Método para vender
  • Linhas 50-53: Executa venda e registra
  • Linha 54: return resultado - Retorna resultado da venda
  • Linha 56: def calcular_comissao(self, taxa=0.05): - Método para calcular comissão
  • Linha 57: return self.vendas * taxa - Retorna comissão
  • Linha 59: def mostrar_info(self): - Método para mostrar informações
  • Linha 60: return f"Funcionário: {self.nome}, Salário: R$ {self.salario:.2f}, Vendas: {self.vendas}" - Retorna informações
  • Linha 62: class Loja: - Define classe Loja
  • Linha 63: def __init__(self, nome): - Construtor da classe Loja
  • Linhas 64-67: Define atributos da loja
  • Linha 68: self.produtos = [] - Lista de produtos
  • Linha 69: self.clientes = [] - Lista de clientes
  • Linha 70: self.funcionarios = [] - Lista de funcionários
  • Linha 72: def adicionar_produto(self, produto): - Método para adicionar produto
  • Linhas 73-74: Adiciona produto à lista
  • Linha 75: return f"Produto {produto.nome} adicionado à loja" - Retorna sucesso
  • Linha 77: def adicionar_cliente(self, cliente): - Método para adicionar cliente
  • Linhas 78-79: Adiciona cliente à lista
  • Linha 80: return f"Cliente {cliente.nome} cadastrado" - Retorna sucesso
  • Linha 82: def adicionar_funcionario(self, funcionario): - Método para adicionar funcionário
  • Linhas 83-84: Adiciona funcionário à lista
  • Linha 85: return f"Funcionário {funcionario.nome} contratado" - Retorna sucesso
  • Linha 87: def buscar_produto(self, nome): - Método para buscar produto
  • Linhas 88-91: Procura produto por nome
  • Linha 92: return None - Retorna None se não encontrar
  • Linha 94: def relatorio_estoque(self): - Método para relatório de estoque
  • Linhas 95-97: Mostra relatório de estoque
  • Linha 99: def relatorio_funcionarios(self): - Método para relatório de funcionários
  • Linhas 100-103: Mostra relatório de funcionários

🎯 Parabéns! Você completou todos os exercícios de Programação Orientada a Objetos! Agora você domina os conceitos fundamentais de POO em Python, incluindo classes, objetos, herança, polimorfismo e muito mais. Continue praticando e explore mais possibilidades! 🚀

14.0.3 Nível 3: Médio (5 exercícios)

14.0.3.1 Exercício 11: Classe com Atributos Privados

Enunciado: Crie uma classe Pessoa com nome (público) e idade (privado). Crie métodos para definir e obter a idade.

Resposta:

class Pessoa:
    def __init__(self, nome, idade):
        self.nome = nome
        self.__idade = idade  # Atributo privado
    
    def definir_idade(self, nova_idade):
        if nova_idade >= 0:
            self.__idade = nova_idade
        else:
            print("Idade inválida!")
    
    def obter_idade(self):
        return self.__idade
    
    def mostrar_info(self):
        print(f"Nome: {self.nome}")
        print(f"Idade: {self.obter_idade()} anos")

# Criar pessoa e testar
pessoa = Pessoa("João", 25)
pessoa.mostrar_info()
pessoa.definir_idade(30)
pessoa.mostrar_info()

Explicação linha por linha:

  • Linha 1: class Pessoa: - Define a classe Pessoa
  • Linha 2: def __init__(self, nome, idade): - Define o construtor
  • Linha 3: self.nome = nome - Atributo público
  • Linha 4: self.__idade = idade - Atributo privado (dois underscores)
  • Linha 6: def definir_idade(self, nova_idade): - Método para definir idade
  • Linhas 7-9: Verifica se idade é válida e define
  • Linhas 10-11: Trata idade inválida
  • Linha 13: def obter_idade(self): - Método para obter idade
  • Linha 14: return self.__idade - Retorna idade privada

14.0.3.2 Exercício 12: Classe com Múltiplos Métodos

Enunciado: Crie uma classe Retangulo com largura e altura. Crie métodos para calcular área, perímetro e verificar se é quadrado.

Resposta:

class Retangulo:
    def __init__(self, largura, altura):
        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)
    
    def eh_quadrado(self):
        return self.largura == self.altura
    
    def redimensionar(self, nova_largura, nova_altura):
        self.largura = nova_largura
        self.altura = nova_altura
    
    def mostrar_info(self):
        print(f"Largura: {self.largura}")
        print(f"Altura: {self.altura}")
        print(f"Área: {self.calcular_area()}")
        print(f"Perímetro: {self.calcular_perimetro()}")
        print(f"É quadrado: {self.eh_quadrado()}")

# Criar retângulo e testar
ret = Retangulo(5, 3)
ret.mostrar_info()
ret.redimensionar(4, 4)
ret.mostrar_info()

Explicação linha por linha:

  • Linha 1: class Retangulo: - Define a classe Retangulo
  • Linha 2: def __init__(self, largura, altura): - Define o construtor
  • Linhas 3-4: Define os atributos largura e altura
  • Linha 6: def calcular_area(self): - Método para calcular área
  • Linha 7: return self.largura * self.altura - Retorna área
  • Linha 9: def calcular_perimetro(self): - Método para calcular perímetro
  • Linha 10: return 2 * (self.largura + self.altura) - Retorna perímetro
  • Linha 12: def eh_quadrado(self): - Método para verificar se é quadrado
  • Linha 13: return self.largura == self.altura - Retorna True se for quadrado

14.0.3.3 Exercício 13: Classe com Validação

Enunciado: Crie uma classe ContaBancaria com número da conta e saldo. Adicione validações para todas as operações.

Resposta:

class ContaBancaria:
    def __init__(self, numero_conta, saldo_inicial=0):
        self.numero_conta = numero_conta
        self.saldo = saldo_inicial
        self.__limite_saque = 1000  # Limite privado
    
    def depositar(self, valor):
        if valor > 0:
            self.saldo += valor
            print(f"Depósito de R$ {valor:.2f} realizado!")
            return True
        else:
            print("Valor inválido para depósito!")
            return False
    
    def sacar(self, valor):
        if valor <= 0:
            print("Valor inválido para saque!")
            return False
        elif valor > self.saldo:
            print("Saldo insuficiente!")
            return False
        elif valor > self.__limite_saque:
            print(f"Valor excede o limite de R$ {self.__limite_saque:.2f}")
            return False
        else:
            self.saldo -= valor
            print(f"Saque de R$ {valor:.2f} realizado!")
            return True
    
    def transferir(self, outra_conta, valor):
        if self.sacar(valor):
            outra_conta.depositar(valor)
            print(f"Transferência de R$ {valor:.2f} realizada!")
            return True
        return False
    
    def mostrar_saldo(self):
        print(f"Saldo atual: R$ {self.saldo:.2f}")

# Criar contas e testar transferência
conta1 = ContaBancaria("12345", 1000)
conta2 = ContaBancaria("67890", 500)
conta1.transferir(conta2, 200)
conta1.mostrar_saldo()
conta2.mostrar_saldo()

Explicação linha por linha:

  • Linha 1: class ContaBancaria: - Define a classe ContaBancaria
  • Linha 2: def __init__(self, numero_conta, saldo_inicial=0): - Define construtor
  • Linhas 3-5: Define atributos incluindo limite privado
  • Linha 7: def depositar(self, valor): - Método para depositar
  • Linhas 8-12: Valida valor e deposita
  • Linhas 13-15: Trata valor inválido
  • Linha 17: def sacar(self, valor): - Método para sacar
  • Linhas 18-28: Validações múltiplas para saque
  • Linha 30: def transferir(self, outra_conta, valor): - Método para transferir
  • Linhas 31-35: Executa transferência entre contas

14.0.3.4 Exercício 14: Classe com Lista de Objetos

Enunciado: Crie uma classe Turma que gerencia uma lista de estudantes. Adicione métodos para adicionar, remover e listar estudantes.

Resposta:

class Estudante:
    def __init__(self, nome, idade, nota):
        self.nome = nome
        self.idade = idade
        self.nota = nota
    
    def mostrar_info(self):
        print(f"Nome: {self.nome}, Idade: {self.idade}, Nota: {self.nota}")

class Turma:
    def __init__(self, nome_turma):
        self.nome_turma = nome_turma
        self.estudantes = []
    
    def adicionar_estudante(self, estudante):
        self.estudantes.append(estudante)
        print(f"Estudante {estudante.nome} adicionado à turma!")
    
    def remover_estudante(self, nome):
        for estudante in self.estudantes:
            if estudante.nome == nome:
                self.estudantes.remove(estudante)
                print(f"Estudante {nome} removido da turma!")
                return True
        print(f"Estudante {nome} não encontrado!")
        return False
    
    def listar_estudantes(self):
        print(f"\n=== Turma: {self.nome_turma} ===")
        for estudante in self.estudantes:
            estudante.mostrar_info()
    
    def calcular_media_turma(self):
        if not self.estudantes:
            return 0
        total = sum(estudante.nota for estudante in self.estudantes)
        return total / len(self.estudantes)

# Criar turma e estudantes
turma = Turma("Python Básico")
est1 = Estudante("Ana", 20, 8.5)
est2 = Estudante("João", 19, 7.5)
turma.adicionar_estudante(est1)
turma.adicionar_estudante(est2)
turma.listar_estudantes()
print(f"Média da turma: {turma.calcular_media_turma():.2f}")

Explicação linha por linha:

  • Linha 1: class Estudante: - Define a classe Estudante
  • Linha 2: def __init__(self, nome, idade, nota): - Define construtor
  • Linhas 3-5: Define atributos do estudante
  • Linha 7: def mostrar_info(self): - Método para mostrar informações
  • Linha 8: print(f"Nome: {self.nome}, Idade: {self.idade}, Nota: {self.nota}") - Mostra info
  • Linha 10: class Turma: - Define a classe Turma
  • Linha 11: def __init__(self, nome_turma): - Define construtor
  • Linhas 12-13: Define atributos da turma
  • Linha 15: def adicionar_estudante(self, estudante): - Método para adicionar
  • Linhas 16-17: Adiciona estudante à lista
  • Linha 19: def remover_estudante(self, nome): - Método para remover
  • Linhas 20-26: Procura e remove estudante por nome

14.0.3.5 Exercício 15: Classe com Métodos Estáticos

Enunciado: Crie uma classe Matematica com métodos estáticos para operações matemáticas básicas.

Resposta:

class Matematica:
    @staticmethod
    def somar(a, b):
        return a + b
    
    @staticmethod
    def subtrair(a, b):
        return a - b
    
    @staticmethod
    def multiplicar(a, b):
        return a * b
    
    @staticmethod
    def dividir(a, b):
        if b == 0:
            return "Erro: divisão por zero!"
        return a / b
    
    @staticmethod
    def potencia(base, expoente):
        return base ** expoente
    
    @staticmethod
    def raiz_quadrada(numero):
        if numero < 0:
            return "Erro: raiz de número negativo!"
        return numero ** 0.5

# Usar métodos estáticos (não precisa criar objeto)
print(Matematica.somar(5, 3))  # 8
print(Matematica.potencia(2, 3))  # 8
print(Matematica.raiz_quadrada(16))  # 4.0
print(Matematica.dividir(10, 0))  # Erro

Explicação linha por linha:

  • Linha 1: class Matematica: - Define a classe Matematica
  • Linha 2: @staticmethod - Decorador para método estático
  • Linha 3: def somar(a, b): - Método estático para somar
  • Linha 4: return a + b - Retorna soma
  • Linha 6: @staticmethod - Decorador para método estático
  • Linha 7: def subtrair(a, b): - Método estático para subtrair
  • Linha 8: return a - b - Retorna subtração
  • Linha 10: @staticmethod - Decorador para método estático
  • Linha 11: def multiplicar(a, b): - Método estático para multiplicar
  • Linha 12: return a * b - Retorna multiplicação
  • Linha 14: @staticmethod - Decorador para método estático
  • Linha 15: def dividir(a, b): - Método estático para dividir
  • Linhas 16-18: Verifica divisão por zero
  • Linha 19: return a / b - Retorna divisão

14.0.4 Nível 4: Difícil (5 exercícios)

14.0.4.1 Exercício 16: Sistema de Herança - Animais

Enunciado: Crie uma classe Animal pai e classes filhas Cachorro e Gato que herdam dela.

Resposta:

class Animal:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
    
    def fazer_som(self):
        return "Algum som"
    
    def dormir(self):
        return f"{self.nome} está dormindo"
    
    def comer(self):
        return f"{self.nome} está comendo"

class Cachorro(Animal):
    def __init__(self, nome, idade, raca):
        super().__init__(nome, idade)  # Chama construtor da classe pai
        self.raca = raca
    
    def fazer_som(self):  # Sobrescreve método da classe pai
        return f"{self.nome} está latindo: Au au!"
    
    def buscar_osso(self):
        return f"{self.nome} está buscando um osso"

class Gato(Animal):
    def __init__(self, nome, idade, cor):
        super().__init__(nome, idade)  # Chama construtor da classe pai
        self.cor = cor
    
    def fazer_som(self):  # Sobrescreve método da classe pai
        return f"{self.nome} está miando: Miau!"
    
    def escalar(self):
        return f"{self.nome} está escalando"

# Criar animais e testar
cachorro = Cachorro("Rex", 3, "Golden Retriever")
gato = Gato("Mimi", 2, "Branco")

print(cachorro.fazer_som())  # Polimorfismo
print(gato.fazer_som())      # Polimorfismo
print(cachorro.buscar_osso())
print(gato.escalar())

Explicação linha por linha:

  • Linha 1: class Animal: - Define classe pai Animal
  • Linha 2: def __init__(self, nome, idade): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def fazer_som(self): - Método que será sobrescrito
  • Linha 7: return "Algum som" - Implementação genérica
  • Linha 9: def dormir(self): - Método herdado
  • Linha 10: return f"{self.nome} está dormindo" - Implementação
  • Linha 12: class Cachorro(Animal): - Classe filha que herda de Animal
  • Linha 13: def __init__(self, nome, idade, raca): - Construtor da classe filha
  • Linha 14: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 15: self.raca = raca - Atributo específico da classe filha
  • Linha 17: def fazer_som(self): - Sobrescreve método da classe pai
  • Linha 18: return f"{self.nome} está latindo: Au au!" - Implementação específica

14.0.4.2 Exercício 17: Sistema de Herança - Veículos

Enunciado: Crie uma classe Veiculo pai e classes filhas Carro e Moto que herdam dela.

Resposta:

class Veiculo:
    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"
    
    def mostrar_info(self):
        return f"{self.marca} {self.modelo} ({self.ano})"

class Carro(Veiculo):
    def __init__(self, marca, modelo, ano, portas):
        super().__init__(marca, modelo, ano)
        self.portas = portas
    
    def abrir_porta(self, numero_porta):
        return f"Porta {numero_porta} aberta"
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base} - {self.portas} portas"

class Moto(Veiculo):
    def __init__(self, marca, modelo, ano, cilindradas):
        super().__init__(marca, modelo, ano)
        self.cilindradas = cilindradas
    
    def empinar(self):
        return "Moto empinando!"
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base} - {self.cilindradas}cc"

# Criar veículos e testar
carro = Carro("Toyota", "Corolla", 2023, 4)
moto = Moto("Honda", "CB600", 2023, 600)

print(carro.mostrar_info())
print(moto.mostrar_info())
print(carro.acelerar(50))
print(moto.empinar())

Explicação linha por linha:

  • Linha 1: class Veiculo: - Define classe pai Veiculo
  • Linha 2: def __init__(self, marca, modelo, ano): - Construtor da classe pai
  • Linhas 3-6: Define atributos comuns
  • Linha 8: def acelerar(self, incremento): - Método para acelerar
  • Linhas 9-10: Aumenta velocidade
  • Linha 11: return f"Velocidade atual: {self.velocidade} km/h" - Retorna velocidade
  • Linha 13: def frear(self, decremento): - Método para frear
  • Linhas 14-15: Diminui velocidade (mínimo 0)
  • Linha 16: return f"Velocidade atual: {self.velocidade} km/h" - Retorna velocidade
  • Linha 18: def mostrar_info(self): - Método para mostrar informações
  • Linha 19: return f"{self.marca} {self.modelo} ({self.ano})" - Retorna info básica
  • Linha 21: class Carro(Veiculo): - Classe filha que herda de Veiculo
  • Linha 22: def __init__(self, marca, modelo, ano, portas): - Construtor da classe filha
  • Linha 23: super().__init__(marca, modelo, ano) - Chama construtor da classe pai
  • Linha 24: self.portas = portas - Atributo específico da classe filha
  • Linha 26: def abrir_porta(self, numero_porta): - Método específico da classe filha
  • Linha 27: return f"Porta {numero_porta} aberta" - Retorna mensagem
  • Linha 29: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 30-31: Chama método da classe pai e adiciona informações específicas

14.0.4.3 Exercício 18: Sistema de Herança - Funcionários

Enunciado: Crie uma classe Funcionario pai e classes filhas Gerente e Vendedor que herdam dela.

Resposta:

class Funcionario:
    def __init__(self, nome, salario_base):
        self.nome = nome
        self.salario_base = salario_base
    
    def calcular_salario(self):
        return self.salario_base
    
    def mostrar_info(self):
        return f"Nome: {self.nome}, Salário: R$ {self.calcular_salario():.2f}"

class Gerente(Funcionario):
    def __init__(self, nome, salario_base, bonus_gerencia):
        super().__init__(nome, salario_base)
        self.bonus_gerencia = bonus_gerencia
    
    def calcular_salario(self):  # Sobrescreve método da classe pai
        return self.salario_base + self.bonus_gerencia
    
    def gerenciar_equipe(self):
        return f"{self.nome} está gerenciando a equipe"

class Vendedor(Funcionario):
    def __init__(self, nome, salario_base, comissao):
        super().__init__(nome, salario_base)
        self.comissao = comissao
        self.vendas_mes = 0
    
    def calcular_salario(self):  # Sobrescreve método da classe pai
        return self.salario_base + (self.vendas_mes * self.comissao)
    
    def vender(self, valor):
        self.vendas_mes += valor
        return f"{self.nome} vendeu R$ {valor:.2f}"
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base}, Vendas do mês: R$ {self.vendas_mes:.2f}"

# Criar funcionários e testar
gerente = Gerente("Maria", 5000, 1000)
vendedor = Vendedor("João", 2000, 0.05)

print(gerente.mostrar_info())
print(vendedor.mostrar_info())
vendedor.vender(10000)
print(vendedor.mostrar_info())
print(gerente.gerenciar_equipe())

Explicação linha por linha:

  • Linha 1: class Funcionario: - Define classe pai Funcionario
  • Linha 2: def __init__(self, nome, salario_base): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def calcular_salario(self): - Método que será sobrescrito
  • Linha 7: return self.salario_base - Implementação básica
  • Linha 9: def mostrar_info(self): - Método para mostrar informações
  • Linha 10: return f"Nome: {self.nome}, Salário: R$ {self.calcular_salario():.2f}" - Retorna info
  • Linha 12: class Gerente(Funcionario): - Classe filha que herda de Funcionario
  • Linha 13: def __init__(self, nome, salario_base, bonus_gerencia): - Construtor da classe filha
  • Linha 14: super().__init__(nome, salario_base) - Chama construtor da classe pai
  • Linha 15: self.bonus_gerencia = bonus_gerencia - Atributo específico
  • Linha 17: def calcular_salario(self): - Sobrescreve método da classe pai
  • Linha 18: return self.salario_base + self.bonus_gerencia - Implementação específica
  • Linha 20: def gerenciar_equipe(self): - Método específico da classe filha
  • Linha 21: return f"{self.nome} está gerenciando a equipe" - Retorna mensagem

14.0.4.4 Exercício 19: Sistema de Herança - Formas Geométricas

Enunciado: Crie uma classe Forma pai e classes filhas Retangulo e Circulo que herdam dela.

Resposta:

import math

class Forma:
    def __init__(self, nome):
        self.nome = nome
    
    def calcular_area(self):
        return "Área não implementada"
    
    def calcular_perimetro(self):
        return "Perímetro não implementado"
    
    def mostrar_info(self):
        return f"Forma: {self.nome}"

class Retangulo(Forma):
    def __init__(self, largura, altura):
        super().__init__("Retângulo")
        self.largura = largura
        self.altura = altura
    
    def calcular_area(self):  # Sobrescreve método da classe pai
        return self.largura * self.altura
    
    def calcular_perimetro(self):  # Sobrescreve método da classe pai
        return 2 * (self.largura + self.altura)
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base}, Largura: {self.largura}, Altura: {self.altura}"

class Circulo(Forma):
    def __init__(self, raio):
        super().__init__("Círculo")
        self.raio = raio
    
    def calcular_area(self):  # Sobrescreve método da classe pai
        return math.pi * self.raio ** 2
    
    def calcular_perimetro(self):  # Sobrescreve método da classe pai
        return 2 * math.pi * self.raio
    
    def mostrar_info(self):  # Sobrescreve método da classe pai
        info_base = super().mostrar_info()
        return f"{info_base}, Raio: {self.raio}"

# Criar formas e testar polimorfismo
formas = [
    Retangulo(5, 3),
    Circulo(4),
    Retangulo(2, 8)
]

for forma in formas:
    print(f"{forma.mostrar_info()}")
    print(f"Área: {forma.calcular_area():.2f}")
    print(f"Perímetro: {forma.calcular_perimetro():.2f}")
    print("-" * 30)

Explicação linha por linha:

  • Linha 1: import math - Importa módulo math para usar π
  • Linha 3: class Forma: - Define classe pai Forma
  • Linha 4: def __init__(self, nome): - Construtor da classe pai
  • Linha 5: self.nome = nome - Define atributo nome
  • Linha 7: def calcular_area(self): - Método que será sobrescrito
  • Linha 8: return "Área não implementada" - Implementação genérica
  • Linha 10: def calcular_perimetro(self): - Método que será sobrescrito
  • Linha 11: return "Perímetro não implementado" - Implementação genérica
  • Linha 13: def mostrar_info(self): - Método para mostrar informações
  • Linha 14: return f"Forma: {self.nome}" - Retorna nome da forma
  • Linha 16: class Retangulo(Forma): - Classe filha que herda de Forma
  • Linha 17: def __init__(self, largura, altura): - Construtor da classe filha
  • Linha 18: super().__init__("Retângulo") - Chama construtor da classe pai
  • Linhas 19-20: Define atributos específicos
  • Linha 22: def calcular_area(self): - Sobrescreve método da classe pai
  • Linha 23: return self.largura * self.altura - Implementação específica
  • Linha 25: def calcular_perimetro(self): - Sobrescreve método da classe pai
  • Linha 26: return 2 * (self.largura + self.altura) - Implementação específica
  • Linha 28: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 29-30: Chama método da classe pai e adiciona informações específicas

14.0.4.5 Exercício 20: Sistema de Herança - Contas Bancárias

Enunciado: Crie uma classe Conta pai e classes filhas ContaCorrente e ContaPoupanca que herdam dela.

Resposta:

class Conta:
    def __init__(self, numero_conta, saldo_inicial=0):
        self.numero_conta = numero_conta
        self.saldo = saldo_inicial
    
    def depositar(self, valor):
        if valor > 0:
            self.saldo += valor
            return f"Depósito de R$ {valor:.2f} realizado!"
        return "Valor inválido para depósito!"
    
    def sacar(self, valor):
        if valor > 0 and valor <= self.saldo:
            self.saldo -= valor
            return f"Saque de R$ {valor:.2f} realizado!"
        return "Saldo insuficiente ou valor inválido!"
    
    def mostrar_saldo(self):
        return f"Saldo atual: R$ {self.saldo:.2f}"

class ContaCorrente(Conta):
    def __init__(self, numero_conta, saldo_inicial=0, limite=1000):
        super().__init__(numero_conta, saldo_inicial)
        self.limite = limite
    
    def sacar(self, valor):  # Sobrescreve método da classe pai
        if valor > 0 and valor <= (self.saldo + self.limite):
            self.saldo -= valor
            return f"Saque de R$ {valor:.2f} realizado!"
        return "Saldo insuficiente ou valor inválido!"
    
    def usar_cheque(self, valor):
        if valor > 0 and valor <= (self.saldo + self.limite):
            self.saldo -= valor
            return f"Cheque de R$ {valor:.2f} usado!"
        return "Saldo insuficiente para usar cheque!"

class ContaPoupanca(Conta):
    def __init__(self, numero_conta, saldo_inicial=0, taxa_juros=0.01):
        super().__init__(numero_conta, saldo_inicial)
        self.taxa_juros = taxa_juros
    
    def aplicar_juros(self):
        juros = self.saldo * self.taxa_juros
        self.saldo += juros
        return f"Juros de R$ {juros:.2f} aplicados!"
    
    def sacar(self, valor):  # Sobrescreve método da classe pai
        if valor > 0 and valor <= self.saldo:
            self.saldo -= valor
            return f"Saque de R$ {valor:.2f} realizado!"
        return "Saldo insuficiente ou valor inválido!"

# Criar contas e testar
conta_corrente = ContaCorrente("12345", 1000, 500)
conta_poupanca = ContaPoupanca("67890", 2000, 0.02)

print(conta_corrente.sacar(1200))  # Usa limite
print(conta_corrente.usar_cheque(300))
print(conta_poupanca.aplicar_juros())
print(conta_poupanca.mostrar_saldo())

Explicação linha por linha:

  • Linha 1: class Conta: - Define classe pai Conta
  • Linha 2: def __init__(self, numero_conta, saldo_inicial=0): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def depositar(self, valor): - Método para depositar
  • Linhas 7-10: Valida valor e deposita
  • Linha 11: return "Valor inválido para depósito!" - Trata valor inválido
  • Linha 13: def sacar(self, valor): - Método que será sobrescrito
  • Linhas 14-17: Valida valor e saldo, saca
  • Linha 18: return "Saldo insuficiente ou valor inválido!" - Trata erro
  • Linha 20: def mostrar_saldo(self): - Método para mostrar saldo
  • Linha 21: return f"Saldo atual: R$ {self.saldo:.2f}" - Retorna saldo
  • Linha 23: class ContaCorrente(Conta): - Classe filha que herda de Conta
  • Linha 24: def __init__(self, numero_conta, saldo_inicial=0, limite=1000): - Construtor da classe filha
  • Linha 25: super().__init__(numero_conta, saldo_inicial) - Chama construtor da classe pai
  • Linha 26: self.limite = limite - Define atributo específico
  • Linha 28: def sacar(self, valor): - Sobrescreve método da classe pai
  • Linhas 29-32: Implementação específica que considera limite
  • Linha 33: return "Saldo insuficiente ou valor inválido!" - Trata erro
  • Linha 35: def usar_cheque(self, valor): - Método específico da classe filha
  • Linhas 36-39: Implementação específica para usar cheque
  • Linha 40: return "Saldo insuficiente para usar cheque!" - Trata erro

14.0.5 Nível 5: Muito Difícil (5 exercícios)

14.0.5.1 Exercício 21: Sistema Completo de Biblioteca

Enunciado: Crie um sistema completo de biblioteca com classes Livro, Usuario e Biblioteca que gerencia empréstimos.

Resposta:

from datetime import datetime, timedelta

class Livro:
    def __init__(self, titulo, autor, isbn):
        self.titulo = titulo
        self.autor = autor
        self.isbn = isbn
        self.disponivel = True
        self.data_emprestimo = None
        self.usuario_emprestimo = None
    
    def emprestar(self, usuario):
        if self.disponivel:
            self.disponivel = False
            self.data_emprestimo = datetime.now()
            self.usuario_emprestimo = usuario
            return f"Livro '{self.titulo}' emprestado para {usuario.nome}"
        return f"Livro '{self.titulo}' não está disponível"
    
    def devolver(self):
        if not self.disponivel:
            self.disponivel = True
            self.data_emprestimo = None
            self.usuario_emprestimo = None
            return f"Livro '{self.titulo}' devolvido"
        return f"Livro '{self.titulo}' já está disponível"
    
    def mostrar_info(self):
        status = "Disponível" if self.disponivel else f"Emprestado para {self.usuario_emprestimo.nome}"
        return f"Título: {self.titulo}, Autor: {self.autor}, Status: {status}"

class Usuario:
    def __init__(self, nome, email):
        self.nome = nome
        self.email = email
        self.livros_emprestados = []
    
    def emprestar_livro(self, livro):
        if len(self.livros_emprestados) < 3:  # Limite de 3 livros
            resultado = livro.emprestar(self)
            if "emprestado" in resultado:
                self.livros_emprestados.append(livro)
            return resultado
        return "Limite de empréstimos atingido (máximo 3 livros)"
    
    def devolver_livro(self, livro):
        if livro in self.livros_emprestados:
            resultado = livro.devolver()
            if "devolvido" in resultado:
                self.livros_emprestados.remove(livro)
            return resultado
        return f"Você não tem o livro '{livro.titulo}' emprestado"
    
    def mostrar_livros_emprestados(self):
        if self.livros_emprestados:
            print(f"Livros emprestados por {self.nome}:")
            for livro in self.livros_emprestados:
                print(f"- {livro.titulo}")
        else:
            print(f"{self.nome} não tem livros emprestados")

class Biblioteca:
    def __init__(self, nome):
        self.nome = nome
        self.livros = []
        self.usuarios = []
    
    def adicionar_livro(self, livro):
        self.livros.append(livro)
        return f"Livro '{livro.titulo}' adicionado à biblioteca"
    
    def adicionar_usuario(self, usuario):
        self.usuarios.append(usuario)
        return f"Usuário {usuario.nome} cadastrado na biblioteca"
    
    def buscar_livro(self, titulo):
        for livro in self.livros:
            if titulo.lower() in livro.titulo.lower():
                return livro
        return None
    
    def listar_livros_disponiveis(self):
        disponiveis = [livro for livro in self.livros if livro.disponivel]
        if disponiveis:
            print("Livros disponíveis:")
            for livro in disponiveis:
                print(f"- {livro.titulo} por {livro.autor}")
        else:
            print("Nenhum livro disponível")
    
    def relatorio_geral(self):
        print(f"\n=== Relatório da Biblioteca {self.nome} ===")
        print(f"Total de livros: {len(self.livros)}")
        print(f"Total de usuários: {len(self.usuarios)}")
        disponiveis = len([livro for livro in self.livros if livro.disponivel])
        print(f"Livros disponíveis: {disponiveis}")
        print(f"Livros emprestados: {len(self.livros) - disponiveis}")

# Criar sistema de biblioteca
biblioteca = Biblioteca("Biblioteca Central")

# Criar livros
livro1 = Livro("Python para Iniciantes", "João Silva", "123456")
livro2 = Livro("Algoritmos e Estruturas de Dados", "Maria Santos", "789012")
livro3 = Livro("Machine Learning", "Pedro Costa", "345678")

# Adicionar livros à biblioteca
biblioteca.adicionar_livro(livro1)
biblioteca.adicionar_livro(livro2)
biblioteca.adicionar_livro(livro3)

# Criar usuários
usuario1 = Usuario("Ana", "ana@email.com")
usuario2 = Usuario("Carlos", "carlos@email.com")

# Cadastrar usuários
biblioteca.adicionar_usuario(usuario1)
biblioteca.adicionar_usuario(usuario2)

# Testar empréstimos
print(usuario1.emprestar_livro(livro1))
print(usuario1.emprestar_livro(livro2))
print(usuario2.emprestar_livro(livro3))

# Mostrar relatório
biblioteca.relatorio_geral()
usuario1.mostrar_livros_emprestados()

Explicação linha por linha:

  • Linha 1: from datetime import datetime, timedelta - Importa módulos para datas
  • Linha 3: class Livro: - Define classe Livro
  • Linha 4: def __init__(self, titulo, autor, isbn): - Construtor da classe Livro
  • Linhas 5-8: Define atributos do livro
  • Linha 9: self.disponivel = True - Inicializa como disponível
  • Linha 10: self.data_emprestimo = None - Inicializa data de empréstimo
  • Linha 11: self.usuario_emprestimo = None - Inicializa usuário do empréstimo
  • Linha 13: def emprestar(self, usuario): - Método para emprestar livro
  • Linhas 14-18: Verifica disponibilidade e empresta
  • Linha 19: return f"Livro '{self.titulo}' emprestado para {usuario.nome}" - Retorna sucesso
  • Linha 20: return f"Livro '{self.titiltulo}' não está disponível" - Retorna erro
  • Linha 22: def devolver(self): - Método para devolver livro
  • Linhas 23-27: Verifica se está emprestado e devolve
  • Linha 28: return f"Livro '{self.titulo}' devolvido" - Retorna sucesso
  • Linha 29: return f"Livro '{self.titulo}' já está disponível" - Retorna erro
  • Linha 31: def mostrar_info(self): - Método para mostrar informações
  • Linhas 32-34: Mostra informações do livro
  • Linha 36: class Usuario: - Define classe Usuario
  • Linha 37: def __init__(self, nome, email): - Construtor da classe Usuario
  • Linhas 38-40: Define atributos do usuário
  • Linha 41: self.livros_emprestados = [] - Lista de livros emprestados
  • Linha 43: def emprestar_livro(self, livro): - Método para emprestar livro
  • Linhas 44-49: Verifica limite e empresta livro
  • Linha 50: return "Limite de empréstimos atingido (máximo 3 livros)" - Retorna erro
  • Linha 52: def devolver_livro(self, livro): - Método para devolver livro
  • Linhas 53-58: Verifica se tem o livro e devolve
  • Linha 59: return f"Você não tem o livro '{livro.titulo}' emprestado" - Retorna erro
  • Linha 61: def mostrar_livros_emprestados(self): - Método para mostrar livros emprestados
  • Linhas 62-67: Mostra lista de livros emprestados
  • Linha 69: class Biblioteca: - Define classe Biblioteca
  • Linha 70: def __init__(self, nome): - Construtor da classe Biblioteca
  • Linhas 71-74: Define atributos da biblioteca
  • Linha 75: self.livros = [] - Lista de livros
  • Linha 76: self.usuarios = [] - Lista de usuários
  • Linha 78: def adicionar_livro(self, livro): - Método para adicionar livro
  • Linhas 79-80: Adiciona livro à lista
  • Linha 81: return f"Livro '{livro.titulo}' adicionado à biblioteca" - Retorna sucesso
  • Linha 83: def adicionar_usuario(self, usuario): - Método para adicionar usuário
  • Linhas 84-85: Adiciona usuário à lista
  • Linha 86: return f"Usuário {usuario.nome} cadastrado na biblioteca" - Retorna sucesso
  • Linha 88: def buscar_livro(self, titulo): - Método para buscar livro
  • Linhas 89-92: Procura livro por título
  • Linha 93: return None - Retorna None se não encontrar
  • Linha 95: def listar_livros_disponiveis(self): - Método para listar livros disponíveis
  • Linhas 96-103: Lista livros disponíveis
  • Linha 105: def relatorio_geral(self): - Método para relatório geral
  • Linhas 106-112: Mostra relatório da biblioteca

14.0.5.2 Exercício 22: Sistema de Herança Múltipla

Enunciado: Crie um sistema com herança múltipla: PessoaFuncionarioGerente e PessoaClienteClienteVip.

Resposta:

class Pessoa:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
    
    def apresentar(self):
        return f"Olá, eu sou {self.nome} e tenho {self.idade} anos"

class Funcionario(Pessoa):
    def __init__(self, nome, idade, salario):
        super().__init__(nome, idade)
        self.salario = salario
    
    def calcular_salario(self):
        return self.salario
    
    def trabalhar(self):
        return f"{self.nome} está trabalhando"

class Gerente(Funcionario):
    def __init__(self, nome, idade, salario, bonus_gerencia):
        super().__init__(nome, idade, salario)
        self.bonus_gerencia = bonus_gerencia
    
    def calcular_salario(self):
        return self.salario + self.bonus_gerencia
    
    def gerenciar_equipe(self):
        return f"{self.nome} está gerenciando a equipe"

class Cliente(Pessoa):
    def __init__(self, nome, idade, email):
        super().__init__(nome, idade)
        self.email = email
        self.compras = []
    
    def comprar(self, produto):
        self.compras.append(produto)
        return f"{self.nome} comprou {produto}"
    
    def mostrar_compras(self):
        if self.compras:
            return f"Compras de {self.nome}: {', '.join(self.compras)}"
        return f"{self.nome} não fez compras ainda"

class ClienteVip(Cliente):
    def __init__(self, nome, idade, email, desconto):
        super().__init__(nome, idade, email)
        self.desconto = desconto
    
    def comprar(self, produto):
        self.compras.append(f"{produto} (com desconto de {self.desconto}%)")
        return f"{self.nome} comprou {produto} com desconto de {self.desconto}%"
    
    def mostrar_desconto(self):
        return f"{self.nome} tem desconto de {self.desconto}%"

# Criar objetos e testar
gerente = Gerente("Maria", 35, 5000, 1000)
cliente_vip = ClienteVip("João", 28, "joao@email.com", 15)

print(gerente.apresentar())
print(gerente.calcular_salario())
print(gerente.gerenciar_equipe())
print(cliente_vip.apresentar())
print(cliente_vip.comprar("Notebook"))
print(cliente_vip.mostrar_desconto())

Explicação linha por linha:

  • Linha 1: class Pessoa: - Define classe pai Pessoa
  • Linha 2: def __init__(self, nome, idade): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def apresentar(self): - Método para apresentar
  • Linha 7: return f"Olá, eu sou {self.nome} e tenho {self.idade} anos" - Retorna apresentação
  • Linha 9: class Funcionario(Pessoa): - Classe filha que herda de Pessoa
  • Linha 10: def __init__(self, nome, idade, salario): - Construtor da classe filha
  • Linha 11: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 12: self.salario = salario - Define atributo específico
  • Linha 14: def calcular_salario(self): - Método para calcular salário
  • Linha 15: return self.salario - Retorna salário
  • Linha 17: def trabalhar(self): - Método para trabalhar
  • Linha 18: return f"{self.nome} está trabalhando" - Retorna mensagem
  • Linha 20: class Gerente(Funcionario): - Classe filha que herda de Funcionario
  • Linha 21: def __init__(self, nome, idade, salario, bonus_gerencia): - Construtor da classe filha
  • Linha 22: super().__init__(nome, idade, salario) - Chama construtor da classe pai
  • Linha 23: self.bonus_gerencia = bonus_gerencia - Define atributo específico
  • Linha 25: def calcular_salario(self): - Sobrescreve método da classe pai
  • Linha 26: return self.salario + self.bonus_gerencia - Implementação específica
  • Linha 28: def gerenciar_equipe(self): - Método específico da classe filha
  • Linha 29: return f"{self.nome} está gerenciando a equipe" - Retorna mensagem
  • Linha 31: class Cliente(Pessoa): - Classe filha que herda de Pessoa
  • Linha 32: def __init__(self, nome, idade, email): - Construtor da classe filha
  • Linha 33: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linhas 34-35: Define atributos específicos
  • Linha 36: self.compras = [] - Lista de compras
  • Linha 38: def comprar(self, produto): - Método para comprar
  • Linhas 39-40: Adiciona produto à lista de compras
  • Linha 41: return f"{self.nome} comprou {produto}" - Retorna mensagem
  • Linha 43: def mostrar_compras(self): - Método para mostrar compras
  • Linhas 44-46: Mostra lista de compras
  • Linha 48: class ClienteVip(Cliente): - Classe filha que herda de Cliente
  • Linha 49: def __init__(self, nome, idade, email, desconto): - Construtor da classe filha
  • Linha 50: super().__init__(nome, idade, email) - Chama construtor da classe pai
  • Linha 51: self.desconto = desconto - Define atributo específico
  • Linha 53: def comprar(self, produto): - Sobrescreve método da classe pai
  • Linhas 54-55: Implementação específica com desconto
  • Linha 56: return f"{self.nome} comprou {produto} com desconto de {self.desconto}%" - Retorna mensagem
  • Linha 58: def mostrar_desconto(self): - Método específico da classe filha
  • Linha 59: return f"{self.nome} tem desconto de {self.desconto}%" - Retorna mensagem

14.0.5.3 Exercício 23: Sistema de Herança com Polimorfismo

Enunciado: Crie um sistema com classes Animal, Cachorro, Gato e Galinha que demonstrem polimorfismo.

Resposta:

class Animal:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
    
    def fazer_som(self):
        return "Algum som"
    
    def mover(self):
        return "Movendo"
    
    def dormir(self):
        return f"{self.nome} está dormindo"
    
    def mostrar_info(self):
        return f"Nome: {self.nome}, Idade: {self.idade} anos"

class Cachorro(Animal):
    def __init__(self, nome, idade, raca):
        super().__init__(nome, idade)
        self.raca = raca
    
    def fazer_som(self):  # Polimorfismo
        return f"{self.nome} está latindo: Au au!"
    
    def mover(self):  # Polimorfismo
        return f"{self.nome} está correndo"
    
    def buscar_osso(self):
        return f"{self.nome} está buscando um osso"

class Gato(Animal):
    def __init__(self, nome, idade, cor):
        super().__init__(nome, idade)
        self.cor = cor
    
    def fazer_som(self):  # Polimorfismo
        return f"{self.nome} está miando: Miau!"
    
    def mover(self):  # Polimorfismo
        return f"{self.nome} está caminhando silenciosamente"
    
    def escalar(self):
        return f"{self.nome} está escalando"

class Galinha(Animal):
    def __init__(self, nome, idade, cor_penas):
        super().__init__(nome, idade)
        self.cor_penas = cor_penas
    
    def fazer_som(self):  # Polimorfismo
        return f"{self.nome} está cacarejando: Cocoricó!"
    
    def mover(self):  # Polimorfismo
        return f"{self.nome} está caminhando"
    
    def botar_ovo(self):
        return f"{self.nome} botou um ovo"

# Criar lista de animais (polimorfismo)
animais = [
    Cachorro("Rex", 3, "Golden Retriever"),
    Gato("Mimi", 2, "Branco"),
    Galinha("Penélope", 1, "Marrom")
]

# Testar polimorfismo
for animal in animais:
    print(f"{animal.mostrar_info()}")
    print(f"Som: {animal.fazer_som()}")
    print(f"Movimento: {animal.mover()}")
    print(f"Sono: {animal.dormir()}")
    print("-" * 40)

# Testar métodos específicos
cachorro = animais[0]
gato = animais[1]
galinha = animais[2]

print(cachorro.buscar_osso())
print(gato.escalar())
print(galinha.botar_ovo())

Explicação linha por linha:

  • Linha 1: class Animal: - Define classe pai Animal
  • Linha 2: def __init__(self, nome, idade): - Construtor da classe pai
  • Linhas 3-4: Define atributos comuns
  • Linha 6: def fazer_som(self): - Método que será sobrescrito
  • Linha 7: return "Algum som" - Implementação genérica
  • Linha 9: def mover(self): - Método que será sobrescrito
  • Linha 10: return "Movendo" - Implementação genérica
  • Linha 12: def dormir(self): - Método herdado
  • Linha 13: return f"{self.nome} está dormindo" - Implementação
  • Linha 15: def mostrar_info(self): - Método para mostrar informações
  • Linha 16: return f"Nome: {self.nome}, Idade: {self.idade} anos" - Retorna informações
  • Linha 18: class Cachorro(Animal): - Classe filha que herda de Animal
  • Linha 19: def __init__(self, nome, idade, raca): - Construtor da classe filha
  • Linha 20: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 21: self.raca = raca - Define atributo específico
  • Linha 23: def fazer_som(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 24: return f"{self.nome} está latindo: Au au!" - Implementação específica
  • Linha 26: def mover(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 27: return f"{self.nome} está correndo" - Implementação específica
  • Linha 29: def buscar_osso(self): - Método específico da classe filha
  • Linha 30: return f"{self.nome} está buscando um osso" - Retorna mensagem
  • Linha 32: class Gato(Animal): - Classe filha que herda de Animal
  • Linha 33: def __init__(self, nome, idade, cor): - Construtor da classe filha
  • Linha 34: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 35: self.cor = cor - Define atributo específico
  • Linha 37: def fazer_som(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 38: return f"{self.nome} está miando: Miau!" - Implementação específica
  • Linha 40: def mover(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 41: return f"{self.nome} está caminhando silenciosamente" - Implementação específica
  • Linha 43: def escalar(self): - Método específico da classe filha
  • Linha 44: return f"{self.nome} está escalando" - Retorna mensagem
  • Linha 46: class Galinha(Animal): - Classe filha que herda de Animal
  • Linha 47: def __init__(self, nome, idade, cor_penas): - Construtor da classe filha
  • Linha 48: super().__init__(nome, idade) - Chama construtor da classe pai
  • Linha 49: self.cor_penas = cor_penas - Define atributo específico
  • Linha 51: def fazer_som(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 52: return f"{self.nome} está cacarejando: Cocoricó!" - Implementação específica
  • Linha 54: def mover(self): - Sobrescreve método da classe pai (polimorfismo)
  • Linha 55: return f"{self.nome} está caminhando" - Implementação específica
  • Linha 57: def botar_ovo(self): - Método específico da classe filha
  • Linha 58: return f"{self.nome} botou um ovo" - Retorna mensagem
  • Linha 60: animais = [...] - Lista com objetos de diferentes classes
  • Linha 64: for animal in animais: - Loop para testar polimorfismo
  • Linhas 65-69: Chama métodos que se comportam diferente para cada tipo

14.0.5.4 Exercício 24: Sistema de Herança com Métodos Abstratos

Enunciado: Crie um sistema com classe abstrata Forma e classes filhas Retangulo e Circulo.

Resposta:

from abc import ABC, abstractmethod
import math

class Forma(ABC):
    def __init__(self, nome):
        self.nome = nome
    
    @abstractmethod
    def calcular_area(self):
        pass
    
    @abstractmethod
    def calcular_perimetro(self):
        pass
    
    def mostrar_info(self):
        return f"Forma: {self.nome}"

class Retangulo(Forma):
    def __init__(self, largura, altura):
        super().__init__("Retângulo")
        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)
    
    def mostrar_info(self):
        info_base = super().mostrar_info()
        return f"{info_base}, Largura: {self.largura}, Altura: {self.altura}"

class Circulo(Forma):
    def __init__(self, raio):
        super().__init__("Círculo")
        self.raio = raio
    
    def calcular_area(self):
        return math.pi * self.raio ** 2
    
    def calcular_perimetro(self):
        return 2 * math.pi * self.raio
    
    def mostrar_info(self):
        info_base = super().mostrar_info()
        return f"{info_base}, Raio: {self.raio}"

# Criar formas e testar
formas = [
    Retangulo(5, 3),
    Circulo(4),
    Retangulo(2, 8)
]

for forma in formas:
    print(f"{forma.mostrar_info()}")
    print(f"Área: {forma.calcular_area():.2f}")
    print(f"Perímetro: {forma.calcular_perimetro():.2f}")
    print("-" * 30)

Explicação linha por linha:

  • Linha 1: from abc import ABC, abstractmethod - Importa módulos para classes abstratas
  • Linha 2: import math - Importa módulo math para usar π
  • Linha 4: class Forma(ABC): - Define classe abstrata Forma
  • Linha 5: def __init__(self, nome): - Construtor da classe abstrata
  • Linha 6: self.nome = nome - Define atributo nome
  • Linha 8: @abstractmethod - Decorador para método abstrato
  • Linha 9: def calcular_area(self): - Método abstrato que deve ser implementado
  • Linha 10: pass - Placeholder para método abstrato
  • Linha 12: @abstractmethod - Decorador para método abstrato
  • Linha 13: def calcular_perimetro(self): - Método abstrato que deve ser implementado
  • Linha 14: pass - Placeholder para método abstrato
  • Linha 16: def mostrar_info(self): - Método concreto da classe abstrata
  • Linha 17: return f"Forma: {self.nome}" - Retorna informações básicas
  • Linha 19: class Retangulo(Forma): - Classe filha que herda de Forma
  • Linha 20: def __init__(self, largura, altura): - Construtor da classe filha
  • Linha 21: super().__init__("Retângulo") - Chama construtor da classe pai
  • Linhas 22-23: Define atributos específicos
  • Linha 25: def calcular_area(self): - Implementa método abstrato
  • Linha 26: return self.largura * self.altura - Implementação específica
  • Linha 28: def calcular_perimetro(self): - Implementa método abstrato
  • Linha 29: return 2 * (self.largura + self.altura) - Implementação específica
  • Linha 31: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 32-33: Chama método da classe pai e adiciona informações específicas
  • Linha 35: class Circulo(Forma): - Classe filha que herda de Forma
  • Linha 36: def __init__(self, raio): - Construtor da classe filha
  • Linha 37: super().__init__("Círculo") - Chama construtor da classe pai
  • Linha 38: self.raio = raio - Define atributo específico
  • Linha 40: def calcular_area(self): - Implementa método abstrato
  • Linha 41: return math.pi * self.raio ** 2 - Implementação específica
  • Linha 43: def calcular_perimetro(self): - Implementa método abstrato
  • Linha 44: return 2 * math.pi * self.raio - Implementação específica
  • Linha 46: def mostrar_info(self): - Sobrescreve método da classe pai
  • Linhas 47-48: Chama método da classe pai e adiciona informações específicas
  • Linha 50: formas = [...] - Lista com objetos de diferentes classes
  • Linha 54: for forma in formas: - Loop para testar polimorfismo
  • Linhas 55-58: Chama métodos que se comportam diferente para cada tipo

14.0.5.5 Exercício 25: Sistema Completo de POO

Enunciado: Crie um sistema completo de gerenciamento de uma loja com classes Produto, Cliente, Funcionario e Loja.

Resposta:

class Produto:
    def __init__(self, nome, preco, estoque):
        self.nome = nome
        self.preco = preco
        self.estoque = estoque
    
    def vender(self, quantidade):
        if quantidade <= self.estoque:
            self.estoque -= quantidade
            return f"Vendidos {quantidade} {self.nome}(s)"
        return f"Estoque insuficiente de {self.nome}"
    
    def repor_estoque(self, quantidade):
        self.estoque += quantidade
        return f"Repostos {quantidade} {self.nome}(s)"
    
    def mostrar_info(self):
        return f"Produto: {self.nome}, Preço: R$ {self.preco:.2f}, Estoque: {self.estoque}"

class Cliente:
    def __init__(self, nome, email):
        self.nome = nome
        self.email = email
        self.compras = []
    
    def comprar(self, produto, quantidade):
        resultado = produto.vender(quantidade)
        if "Vendidos" in resultado:
            self.compras.append(f"{quantidade} {produto.nome}")
        return resultado
    
    def mostrar_compras(self):
        if self.compras:
            return f"Compras de {self.nome}: {', '.join(self.compras)}"
        return f"{self.nome} não fez compras ainda"

class Funcionario:
    def __init__(self, nome, salario):
        self.nome = nome
        self.salario = salario
        self.vendas = 0
    
    def vender(self, produto, quantidade):
        resultado = produto.vender(quantidade)
        if "Vendidos" in resultado:
            self.vendas += quantidade
        return resultado
    
    def calcular_comissao(self, taxa=0.05):
        return self.vendas * taxa
    
    def mostrar_info(self):
        return f"Funcionário: {self.nome}, Salário: R$ {self.salario:.2f}, Vendas: {self.vendas}"

class Loja:
    def __init__(self, nome):
        self.nome = nome
        self.produtos = []
        self.clientes = []
        self.funcionarios = []
    
    def adicionar_produto(self, produto):
        self.produtos.append(produto)
        return f"Produto {produto.nome} adicionado à loja"
    
    def adicionar_cliente(self, cliente):
        self.clientes.append(cliente)
        return f"Cliente {cliente.nome} cadastrado"
    
    def adicionar_funcionario(self, funcionario):
        self.funcionarios.append(funcionario)
        return f"Funcionário {funcionario.nome} contratado"
    
    def buscar_produto(self, nome):
        for produto in self.produtos:
            if nome.lower() in produto.nome.lower():
                return produto
        return None
    
    def relatorio_estoque(self):
        print(f"\n=== Relatório de Estoque - {self.nome} ===")
        for produto in self.produtos:
            print(produto.mostrar_info())
    
    def relatorio_funcionarios(self):
        print(f"\n=== Relatório de Funcionários - {self.nome} ===")
        for funcionario in self.funcionarios:
            print(funcionario.mostrar_info())
            print(f"Comissão: R$ {funcionario.calcular_comissao():.2f}")

# Criar sistema de loja
loja = Loja("Loja Python")

# Criar produtos
produto1 = Produto("Notebook", 2000, 10)
produto2 = Produto("Mouse", 50, 50)
produto3 = Produto("Teclado", 100, 30)

# Adicionar produtos à loja
loja.adicionar_produto(produto1)
loja.adicionar_produto(produto2)
loja.adicionar_produto(produto3)

# Criar clientes
cliente1 = Cliente("Ana", "ana@email.com")
cliente2 = Cliente("João", "joao@email.com")

# Cadastrar clientes
loja.adicionar_cliente(cliente1)
loja.adicionar_cliente(cliente2)

# Criar funcionários
funcionario1 = Funcionario("Maria", 3000)
funcionario2 = Funcionario("Pedro", 2500)

# Contratar funcionários
loja.adicionar_funcionario(funcionario1)
loja.adicionar_funcionario(funcionario2)

# Testar vendas
print(funcionario1.vender(produto1, 2))
print(funcionario2.vender(produto2, 5))
print(cliente1.comprar(produto3, 1))

# Mostrar relatórios
loja.relatorio_estoque()
loja.relatorio_funcionarios()

Explicação linha por linha:

  • Linha 1: class Produto: - Define classe Produto
  • Linha 2: def __init__(self, nome, preco, estoque): - Construtor da classe Produto
  • Linhas 3-5: Define atributos do produto
  • Linha 7: def vender(self, quantidade): - Método para vender produto
  • Linhas 8-11: Verifica estoque e vende
  • Linha 12: return f"Estoque insuficiente de {self.nome}" - Retorna erro
  • Linha 14: def repor_estoque(self, quantidade): - Método para repor estoque
  • Linhas 15-16: Adiciona quantidade ao estoque
  • Linha 17: return f"Repostos {quantidade} {self.nome}(s)" - Retorna sucesso
  • Linha 19: def mostrar_info(self): - Método para mostrar informações
  • Linha 20: return f"Produto: {self.nome}, Preço: R$ {self.preco:.2f}, Estoque: {self.estoque}" - Retorna informações
  • Linha 22: class Cliente: - Define classe Cliente
  • Linha 23: def __init__(self, nome, email): - Construtor da classe Cliente
  • Linhas 24-26: Define atributos do cliente
  • Linha 27: self.compras = [] - Lista de compras
  • Linha 29: def comprar(self, produto, quantidade): - Método para comprar
  • Linhas 30-33: Executa compra e registra
  • Linha 34: return resultado - Retorna resultado da compra
  • Linha 36: def mostrar_compras(self): - Método para mostrar compras
  • Linhas 37-40: Mostra lista de compras
  • Linha 42: class Funcionario: - Define classe Funcionario
  • Linha 43: def __init__(self, nome, salario): - Construtor da classe Funcionario
  • Linhas 44-46: Define atributos do funcionário
  • Linha 47: self.vendas = 0 - Contador de vendas
  • Linha 49: def vender(self, produto, quantidade): - Método para vender
  • Linhas 50-53: Executa venda e registra
  • Linha 54: return resultado - Retorna resultado da venda
  • Linha 56: def calcular_comissao(self, taxa=0.05): - Método para calcular comissão
  • Linha 57: return self.vendas * taxa - Retorna comissão
  • Linha 59: def mostrar_info(self): - Método para mostrar informações
  • Linha 60: return f"Funcionário: {self.nome}, Salário: R$ {self.salario:.2f}, Vendas: {self.vendas}" - Retorna informações
  • Linha 62: class Loja: - Define classe Loja
  • Linha 63: def __init__(self, nome): - Construtor da classe Loja
  • Linhas 64-67: Define atributos da loja
  • Linha 68: self.produtos = [] - Lista de produtos
  • Linha 69: self.clientes = [] - Lista de clientes
  • Linha 70: self.funcionarios = [] - Lista de funcionários
  • Linha 72: def adicionar_produto(self, produto): - Método para adicionar produto
  • Linhas 73-74: Adiciona produto à lista
  • Linha 75: return f"Produto {produto.nome} adicionado à loja" - Retorna sucesso
  • Linha 77: def adicionar_cliente(self, cliente): - Método para adicionar cliente
  • Linhas 78-79: Adiciona cliente à lista
  • Linha 80: return f"Cliente {cliente.nome} cadastrado" - Retorna sucesso
  • Linha 82: def adicionar_funcionario(self, funcionario): - Método para adicionar funcionário
  • Linhas 83-84: Adiciona funcionário à lista
  • Linha 85: return f"Funcionário {funcionario.nome} contratado" - Retorna sucesso
  • Linha 87: def buscar_produto(self, nome): - Método para buscar produto
  • Linhas 88-91: Procura produto por nome
  • Linha 92: return None - Retorna None se não encontrar
  • Linha 94: def relatorio_estoque(self): - Método para relatório de estoque
  • Linhas 95-97: Mostra relatório de estoque
  • Linha 99: def relatorio_funcionarios(self): - Método para relatório de funcionários
  • Linhas 100-103: Mostra relatório de funcionários

🎯 Parabéns! Você completou todos os exercícios de Programação Orientada a Objetos! Agora você domina os conceitos fundamentais de POO em Python, incluindo classes, objetos, herança, polimorfismo e muito mais. Continue praticando e explore mais possibilidades! 🚀

1. Crie uma classe chamada Animal com atributos nome e especie. Crie um método para mostrar na tela o som que o animal faz. Crie um objeto para demonstrar o seu uso.

class Animal:
    def __init__(self, nome, especie):
        self.nome = nome
        self.especie = especie
    
    def fazer_som(self):
        print("{} está fazendo um som de {}.".format(self.nome, self.especie))

# Cria um objeto da classe Animal
meu_animal = Animal("Kora", "cachorro")

# Chama o método som do objeto criado
meu_animal.fazer_som()

Este código define uma classe chamada Animal com dois atributos: nome e especie. Além disso, há um método chamado fazer_som() que imprime na tela o som que o animal está fazendo, utilizando as informações dos atributos nome e especie. O self é uma referência para a instância da classe, permitindo acessar os atributos e métodos da mesma.

Para exemplificar, criamos objeto chamado meu_animal a partir da classe Animal, passando "Kora" como o nome do animal e "cão" como a espécie. Para testar isso, usamos o método fazer_som() do objeto meu_animal, que imprime na tela o som que o animal está fazendo. Neste caso, a saída será "Kora está fazendo um som de cachorro.".

2. Crie uma classe Calculadora com os métodos somar, subtrair, multiplicar e dividir. Crie um objeto dessa classe e utilize os métodos para realizar operações aritméticas simples.

class Calculadora:
    def somar(self, a, b):
        return a + b
    
    def subtrair(self, a, b):
        return a - b
    
    def multiplicar(self, a, b):
        return a * b
    
    def dividir(self, a, b):
        if b == 0:
            return "Não é possível dividir por zero"
        else:
            return a / b
        
# Cria um objeto da classe Calculadora
minha_calculadora = Calculadora()

# Realiza algumas operações aritméticas simples
print(minha_calculadora.somar(2, 3))  # Imprime 5
print(minha_calculadora.subtrair(5, 2))  # Imprime 3
print(minha_calculadora.multiplicar(4, 6))  # Imprime 24
print(minha_calculadora.dividir(10, 5))  # Imprime 2.0
print(minha_calculadora.dividir(10, 0))  # Imprime "Não é possível dividir por zero"

Percebeu que este código não tem o construtor (def __init__)? O código não tem construtor porque ele não precisa de nenhum atributo específico para funcionar corretamente. Todas as operações aritméticas podem ser realizadas apenas com os parâmetros de entrada dos métodos. Por isso, não é necessário criar um construtor que inicialize algum atributo da classe.

Veja que este é um caso específico em que não precisamos guardar nenhum atributo antes para calcular. Como uma calculadora é usada para testar diferentes combinações de valores, o propósito dela realmente não precisa de nenhum construtor neste cenário.

3. Crie uma classe Funcionario com os atributos nome e salario. Em seguida, crie um método para calcular o salário anual do funcionário, incluindo o décimo-terceiro salário. Crie um objeto dessa classe com salário definido e exiba na tela o salário anual.

class Funcionario:
    def __init__(self, nome, salario):
        self.nome = nome
        self.salario = salario
    
    def calcular_salario_anual(self):
        return self.salario * 13
    
# Cria um objeto da classe Funcionario
funcionario1 = Funcionario("João", 3000)

# Calcula o salário anual do funcionário
salario_anual = funcionario1.calcular_salario_anual()

# Exibe na tela o salário anual do funcionário
print("O salário anual do funcionário {} é R$ {:.2f}".format(funcionario1.nome, salario_anual))

Veja que este código possui uma classe chamada Funcionario, um método construtor que recebe o nome e o salario de um funcionário e, ainda, um método especializado em calcular o salário anual levando em consideração o décimo-terceiro salário (calcular_salario_anual()). Se testarmos um objeto da classe Funcionario com nome "João" e R$ 3000,00 (3000), o método calcular_salario_anual() retornará o valor 39000. Este resultado é mostrado no final em uma frase assim: "O salário anual do funcionário João é R$ 39000.00".

4. Crie uma classe Estudante com os atributos nome, idade e nota. Em seguida, crie um método para calcular a média das notas de dois alunos. Crie dois objetos dessa classe com notas definidas e exiba na tela a média calculada.

class Estudante:
    def __init__(self, nome, idade, nota):
        self.nome = nome
        self.idade = idade
        self.nota = nota

    def calcular_media(self, outro_estudante):
        media = (self.nota + outro_estudante.nota) / 2
        return media

# Cria os objetos da classe Estudante
estudante1 = Estudante("Ana", 20, 8.5)
estudante2 = Estudante("João", 19, 7.5)

# Chama o método calcular_media para calcular a média das notas dos dois estudantes
media_notas = estudante1.calcular_media(estudante2)

# Exibe a média calculada na tela
print("A média das notas dos estudantes {} e {} é: {}".format(estudante1.nome, estudante2.nome, media_notas))

Aqui, criamos uma classe Estudante com os atributos nome, idade e nota. Em seguida, criamos o método calcular_media() que recebe como parâmetro outro objeto da classe Estudante, e que calcula a média das notas do objeto atual e do objeto passado como parâmetro (percebe o outro_estudante?).

São criados dois objetos da classe Estudante com notas definidas e é chamado o método calcular_media() para calcular a média das notas desses dos dois estudantes. Neste caso em específico, a seguinte mensagem seria mostrada na tela: "A média das notas dos estudantes Ana e João é: 8.0".

5. Crie uma classe chamada Livro com atributos titulo, autor e ano_publicacao. Crie um método para imprimir as informações do livro. Crie um objeto para demonstrar o seu uso.

class Livro:
    def __init__(self, titulo_informado, autor_informado, ano_publicacao_informado):
        self.titulo = titulo_informado
        self.autor = autor_informado
        self.ano_publicacao = ano_publicacao_informado
    
    def mostrar_informacoes(self):
        print("Título: {}".format(self.titulo))
        print("Autor: {}".format(self.autor))
        print("Ano de publicação: {}".format(self.ano_publicacao))

# Cria um objeto da classe Livro
meu_livro = Livro("O Senhor dos Anéis: A Sociedade do Anel", "J. R. R. Tolkien", 1954)

# Chama o método imprimir_info do objeto criado
meu_livro.mostrar_informacoes()

Veja que criamos a classe Livro com três atributos no construtor: titulo, autor e ano_publicacao. Temos também um método: mostrar_informacoes(), responsável por mostrar as informações do livro na tela (nome sugestivo, não?). Depois, criamos um objeto da classe Livro com as informações do livro “O Senhor dos Anéis: A Sociedade do Anel”, e testamos com o método mostrar_informacoes().

Aqui, usei no construtor uns nomes um pouco modificados (titulo_informado, autor_informado, e ano_publicacao_informado) para que você perceba que essas variáveis são coisas diferentes em relação a self.titulo, self.autor e self.ano_publicacao.

14.1 Referências bibliográficas

  • DOWNEY, A. B. Think Python: How to Think Like a Computer Scientist (2ª ed.). O’Reilly Media, 2015.
  • MICROSOFT. Herança em C#. Disponível em: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/inheritance. Acesso em 14 mar. 2023.
  • ORACLE. Site da Oracle sobre herança em Java. Disponível em: https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html. Acesso em: 14 mar. 2023.
  • PYTHON. Documentação oficial. Classes. Disponível em: https://docs.python.org/3/tutorial/classes.html. Acesso em 14 mar. 2023.
  • PYTHON. Documentação oficial. Polimorfismo. Disponível em: https://docs.python.org/3/library/functions.html#callable. Acesso em 14 mar. 2023.
  • PROGRAMIZ. Class and Objects. Disponível em: https://www.programiz.com/python-programming/class. Acesso em 14 mar. 2023.
  • PROGRAMIZ. Object-Oriented Programming. Disponível em: https://www.programiz.com/python-programming/object-oriented-programming. Acesso em 14 mar. 2023.
  • SCHILD, H. Java: A Beginner’s Guide, Seventh Edition. McGraw Hill Professional, 2018.
  • SIERRA, K.; BATES, B. Head First Java: A Brain-Friendly Guide. O’Reilly Media, Inc., 2005.
  • VANDERPLAS, J. Python Data Science Handbook. Disponível em: https://jakevdp.github.io/PythonDataScienceHandbook/. Acesso em: 14 mar. 2023
  • W3SCHOOLS. Python Classes. Disponível em: https://www.w3schools.com/python/python_classes.asp. Acesso em 14 mar. 2023.