Classes e objetos Python: tutorial OOP com exemplos reais

  1. Início
  2. Blog
  3. Desenvolvimento de software

Desenvolvimento de Software

Classes e objetos Python: Tutorial OOP com exemplos reais

Aprenda Python orientado a objetos construindo algo real. Crie classes, entenda a si mesmo e __init__, compartilhe comportamento com herança, proteja dados com propriedades e saiba quando uma classe é a ferramenta errada.

10 min de leitura
Classes e objetos Python: tutorial OOP com exemplos reais

Principais pontos

  • Uma classe é um projeto e um objeto é algo construído a partir dele.
  • __init__ configura cada novo objeto e self refere-se a esse objeto.
  • Os atributos definidos na classe são compartilhados. Os atributos definidos em self pertencem a um objeto.
  • Use @dataclass para classes que contêm dados e prefira funções simples quando não houver estado para manter.

À medida que um script cresce, você acaba passando o mesmo grupo de variáveis ​​entre funções e fica difícil dizer quais dados pertencem a qual lógica. Classes e objetos Python resolvem isso agrupando dados e comportamento em uma unidade.

Neste tutorial construímos uma pequena conta bancária passo a passo e adicionamos cada ideia OOP somente quando ela resolve um problema. Executamos todos os exemplos no Python 3.13 e a saída mostrada vem dessa execução. Se você é novo na linguagem, leia nosso tutorial Python para iniciantes primeiro.

O que são classes e objetos?

Uma classe descreve o que algo é e o que pode fazer. Um objeto, também chamado de instância, é aquele específico criado a partir dessa descrição. “Conta” é uma classe, enquanto a conta de Asha e a conta de Ben são dois objetos.

Prazo Significado Exemplo
Classe O projeto Conta
Objeto ou instância Uma coisa feita com isso a = Conta("Asha")
Atributo Dados sobre o objeto a.balance
Método Uma função na classe a.deposit(50)

Você tem usado objetos o tempo todo. Em Python, strings, listas e até funções são objetos, e é por isso que você pode escrever "hello".upper().

Sua primeira aula

class Account:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("amount must be positive")
        self.balance += amount
        return self.balance

a = Account("Asha", 100)
b = Account("Ben")

a.deposit(50)
print(a.balance, b.balance)

# 150 0

__init__ é executado automaticamente sempre que você cria um objeto e define o estado inicial. O depósito em a não afetou b, pois cada objeto possui seus próprios dados. Observe que o método também protege o saldo de um valor ruim. Nosso guia para tratamento de exceções em Python explica raise com mais detalhes.

O que é eu?

self é simplesmente o objeto no qual um método está trabalhando. Quando você escreve a.deposit(50), o Python efetivamente chama Account.deposit(a, 50), passando o objeto como primeiro argumento. É assim que uma definição de método pode servir a todos os objetos.

O nome self é uma convenção forte e não uma palavra-chave, mas sempre use-a. Esquecê-lo na definição de um método é o erro mais comum para iniciantes e produz uma mensagem confusa sobre o número de argumentos.

Atributos de classe versus atributos de instância

Um atributo definido diretamente no corpo da classe é compartilhado por todos os objetos. Um conjunto em self pertence apenas a esse objeto. Este exemplo mostra como as pesquisas se comportam:

class Account:
    interest_rate = 0.04    # class attribute

    def __init__(self, owner, balance=0):
        self.owner = owner  # instance attribute
        self.balance = balance

a = Account("Asha", 100)
b = Account("Ben")
print(a.interest_rate, b.interest_rate)   # 0.04 0.04

Account.interest_rate = 0.05
print(a.interest_rate, b.interest_rate)   # 0.05 0.05

a.interest_rate = 0.09
print(a.interest_rate, b.interest_rate, Account.interest_rate)
# 0.09 0.05 0.05

Essa última linha surpreende as pessoas. Atribuir por meio de a não alterou o valor compartilhado. Ele criou um novo atributo de instância em a que oculta o da classe. Python examina primeiro o objeto e depois a classe.

Métodos especiais (dunder)

Métodos com sublinhados duplos permitem que seus objetos funcionem com os recursos integrados do Python. Sem eles, a impressão de uma conta mostra um endereço de memória inútil. Adicionar dois fornece um formato legível:

    def __str__(self):
        return f"{self.owner}: {self.balance}"

    def __repr__(self):
        return f"Account({self.owner!r}, {self.balance})"

print(a)        # Asha: 150
print([a, b])   # [Account('Asha', 150), Account('Ben', 0)]
Método Acionado por
__init__ Criando um objeto
__str__ print(obj), str(obj)
__repr__ Depurando ou mostrando em uma lista
__eq__ uma == b
__len__ len(obj)
__add__ um + b

Herança

A herança permite que uma nova classe reutilize uma classe existente e altere apenas o que for diferente. Aqui, Savings é um Account com uma configuração extra:

class Savings(Account):
    def __init__(self, owner, balance=0, limit=3):
        super().__init__(owner, balance)
        self.limit = limit

    def __str__(self):
        return f"{super().__str__()} (savings)"

s = Savings("Chitra", 500)
s.deposit(100)
print(s, isinstance(s, Account), s.limit)

# Chitra: 600 (savings) True 3

super() chama a versão pai, para que a lógica de configuração não seja repetida. deposit foi herdado como estava e __str__ foi substituído para adicionar um rótulo. Use herança apenas para um relacionamento “é um” real. Se uma classe apenas precisa de algo, como um carro precisando de um motor, dê a ela esse objeto como um atributo. Isso é conhecido como composição e geralmente é mais flexível do que cadeias de herança profundas.

Polimorfismo

Polimorfismo significa que objetos diferentes podem responder à mesma chamada de método à sua maneira. O código de chamada não precisa saber o tipo exato:

class Square:
    def __init__(self, side):
        self.side = side
    def area(self):
        return self.side ** 2

class Circle:
    def __init__(self, radius):
        self.radius = radius
    def area(self):
        return round(3.14159 * self.radius ** 2, 2)

for shape in [Square(3), Circle(2)]:
    print(type(shape).__name__, shape.area())

# Square 9
# Circle 12.57

Essas duas classes nem sequer compartilham um pai. Python só se importa que o objeto tenha um método area, uma abordagem conhecida como duck tipagem. Adicionar um Triangle posteriormente não precisaria de alterações no loop.

Encapsulamento e propriedades

Python não possui atributos verdadeiramente privados e, em vez disso, depende de convenções. Um único sublinhado, como em _price, diz aos outros desenvolvedores “isso é interno, por favor, não toque”. Para verificar os valores quando eles são definidos, use uma propriedade, que parece um atributo simples visto de fora:

class Product:
    def __init__(self, price):
        self.price = price

    @property
    def price(self):
        return self._price

    @price.setter
    def price(self, value):
        if value < 0:
            raise ValueError("price cannot be negative")
        self._price = value

p = Product(100)
p.price = 150
print(p.price)      # 150
p.price = -5        # ValueError: price cannot be negative

Isso significa que você pode começar com um atributo simples e adicionar validação posteriormente sem alterar nenhum código que use a classe. Portanto, não escreva métodos get_price() e set_price() por hábito, como faria em Java. A sintaxe @ aqui é um decorador, que explicamos em Decoradores Python explicados.

Métodos de classe e métodos estáticos

class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

    @classmethod
    def from_fahrenheit(cls, f):
        return cls(round((f - 32) * 5 / 9, 1))

    @staticmethod
    def is_freezing(celsius):
        return celsius <= 0

Temperature.from_fahrenheit(212).celsius   # 100.0
Temperature.is_freezing(-3)                # True
Tipo Primeiro argumento Use-o para
Método de instância eu mesmo Trabalhando com dados de um objeto
@classmethod cls Construtores alternativos
@staticmethod Nenhum Ajudantes relacionados à classe

Classes de dados

Quando uma classe contém principalmente dados, escrever __init__, __repr__ e __eq__ manualmente é entediante. @dataclass os gera a partir de anotações de tipo:

from dataclasses import dataclass, field

@dataclass
class Item:
    name: str
    price: int
    tags: list = field(default_factory=list)

print(Item("Pen", 10))
print(Item("Pen", 10) == Item("Pen", 10))

# Item(name='Pen', price=10, tags=[])
# True

Dica: Nem tudo precisa de uma aula. Se não tiver estado e apenas um comportamento, uma função simples é mais simples. Para pesquisas simples, um dicionário pode ser suficiente. Alcance uma classe quando os dados e as regras que os protegem claramente pertencem um ao outro.

Erros comuns

Usando um atributo de classe mutável

Este é um bug clássico. A lista é definida na classe, então todos os carrinhos a compartilham:

class Cart:
    items = []            # shared by every cart!

    def add(self, item):
        self.items.append(item)

c1, c2 = Cart(), Cart()
c1.add("pen")
print(c2.items)

# ['pen']

Crie-o em __init__ com self.items = [], para que cada objeto obtenha sua própria lista.

Outros erros comuns

  • Esquecendo self. Escreva self.balance, não balance, dentro dos métodos.
  • Esquecimento super().__init__(). O filho então perde os atributos configurados pelo pai.
  • Construindo classes enormes. Uma classe que faz tudo é difícil de testar. Divida por responsabilidade.
  • Herança profunda. Dois níveis geralmente são suficientes e a composição costuma ser mais clara.
  • Não escrever testes. Classes com regras são fáceis de testar, como mostra nosso tutorial pytest usando um carrinho pequeno.

Considerações finais

Python orientado a objetos se resume a agrupar dados com o comportamento que os protege. Comece com __init__, métodos e __str__ e, em seguida, adicione herança e propriedades quando encontrar um problema que eles resolvam. Na próxima etapa, pegue um script que passe as mesmas variáveis ​​em todos os lugares e transforme-as em uma pequena classe.

Perguntas frequentes

Qual é a diferença entre uma classe e um objeto?

Uma classe é a definição ou modelo. Um objeto é uma instância específica criada a partir dele, com seus próprios dados. Uma classe pode criar muitos objetos.

O que __init__ faz em Python?

É o inicializador, que é executado automaticamente quando você cria um objeto. Geralmente define os atributos iniciais do objeto a partir dos argumentos que você passa.

O que é self em Python?

self refere-se ao objeto no qual um método é chamado. Python passa automaticamente e permite que o método leia e altere os atributos desse objeto.

Quais são os quatro pilares da OOP?

Eles são encapsulamento, abstração, herança e polimorfismo. Juntos, eles ajudam você a agrupar dados com comportamento, ocultar detalhes e reutilizar código.

Quando devo usar uma classe em vez de uma função?

Use uma classe quando os dados e o comportamento estiverem juntos em diversas operações. Para uma operação sem estado, uma função simples é mais simples e fácil de testar.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Teste de software

Teste de aceitação do usuário (UAT): processo, lista de verificação e exemplos reais

Explicação do teste de aceitação do usuário: quem o executa, como ele difere do controle de qualidade, um processo de sete etapas, um modelo de caso…

Leia o artigo →
Bancos de dados e SQL

Valores SQL NULL explicados: IS NULL, COALESCE, NULLIF e as armadilhas a serem evitadas

Valores SQL NULL explicados com consultas que executamos: lógica de três valores, IS NULL, COALESCE, NULLIF, como agregações, junções e classificaçã…

Leia o artigo →
Bancos de dados e SQL

Instrução SQL CASE WHEN: sintaxe, exemplos reais e erros comuns

SQL CASE WHEN explicado com consultas que executamos: formulários simples e pesquisados, ordem de condição, pivôs com SUM, truques COUNT, classifica…

Leia o artigo →
Desenvolvimento de software

O que é JWT? JSON Web Tokens explicados com um exemplo prático

O que é JWT? JSON Web Tokens explicados: as três partes, um token construído e adulterado no código que executamos, JWT vs sessões, armazenamento, t…

Leia o artigo →
Desenvolvimento de software

Loop de eventos JavaScript explicado: pilha de chamadas, microtarefas e temporizadores com exemplos

O loop de eventos JavaScript explicado com o código que executamos: pilha de chamadas, filas de tarefas e microtarefas, por que setTimeout 0 não é i…

Leia o artigo →
Desenvolvimento de software

Geradores Python e rendimento explicado: economize memória com exemplos reais

Geradores Python e rendimento explicados com o código que executamos: como eles pausam e reiniciam, medem economia de memória, pipelines, sequências…

Leia o artigo →
Inteligência artificial

O que são incorporações? Similaridade de vetores e cossenos explicada de forma simples

O que são incorporações? Um guia em linguagem simples para vetores, similaridade de cossenos e pesquisa semântica, com um pequeno exemplo que execut…

Leia o artigo →
DevOps

Implantação azul-verde vs canário: diferenças, compensações e como escolher

Explicação da implantação azul-verde versus canário: como cada um funciona, custo, risco e reversão, agrupamento estável de usuários que testamos, m…

Leia o artigo →
Automação de testes

Testes instáveis: o que os causa e como corrigi-los para sempre

Testes instáveis explicados com demonstrações que executamos: as principais causas, como confirmar e diagnosticar uma, soluções adequadas para tempo…

Leia o artigo →
Teste de software

Teste de carga versus teste de estresse: diferenças, métricas e exemplos k6

Teste de carga versus teste de estresse explicado: o que cada um prova, testes de pico e absorção, as métricas que importam, por que as médias engan…

Leia o artigo →
Teste de software

Caso de teste versus cenário de teste: principais diferenças, exemplos e quando usar cada um

Caso de teste vs cenário de teste explicado: definições claras, uma tabela lado a lado, um recurso de redefinição de senha retirado de cenários para…

Leia o artigo →
Bancos de dados e SQL

Otimização de consultas SQL: leia planos de execução e corrija consultas lentas, com intervalos

Otimização de consulta SQL testada em um milhão de linhas: leia planos de execução, adicione os índices corretos, corrija filtros não sargáveis, use…

Leia o artigo →
Bancos de dados e SQL

SQL UNION vs UNION ALL: diferenças, exemplos e desempenho testado

SQL UNION vs UNION ALL explicado com consultas que executamos: duplicatas, as regras que ambos seguem, um bug de dados real, desempenho medido, INTE…

Leia o artigo →
Desenvolvimento de software

Tutorial da API JavaScript Fetch: GET, POST, tratamento de erros e tempos limite

Um tutorial da API JavaScript Fetch testado em um servidor local: GET e POST, por que 404 não lança, um auxiliar reutilizável, strings de consulta, …

Leia o artigo →
Desenvolvimento de software

Manipulação de arquivos Python: leitura, gravação, CSV, JSON e pathlib com exemplos

Manipulação de arquivos Python explicada com código que executamos: modos abertos, com, leitura de arquivos grandes, CSV, JSON, pathlib, arquivos bi…

Leia o artigo →
Desenvolvimento de software

Classes e objetos Python: tutorial OOP com exemplos reais

Classes e objetos Python explicados com código que executamos: __init__, métodos, atributos de classe vs instância, herança, propriedades, métodos d…

Leia o artigo →
Inteligência artificial

O que é um modelo de linguagem grande? Como funcionam os LLMs, explicado sem exageros

O que é um modelo de linguagem grande? LLMs explicados em linguagem simples: tokens, incorporações, atenção, treinamento, temperatura e janelas de c…

Leia o artigo →
DevOps

Comandos Linux para iniciantes: o guia essencial do terminal com exemplos

Comandos Linux para iniciantes, agrupados por tarefa: navegação, arquivos, visualização de logs, grep, pipes, permissões, processos e arquivos, com …

Leia o artigo →
Automação de testes

Selenium vs Playwright: principais diferenças, código comparado e qual escolher

Selenium vs Playwright comparado: arquitetura, espera, suporte a navegador e linguagem, depuração, o mesmo teste em ambas as ferramentas, conselhos …

Leia o artigo →
Teste de software

Particionamento de equivalência: técnica de design de teste explicada com exemplos resolvidos

Explicação do particionamento de equivalência: como encontrar partições válidas e inválidas, exemplos resolvidos para idades, senhas e cupons, teste…

Leia o artigo →
Teste de software

Gravidade vs Prioridade em Testes de Software: Diferenças, Matriz e Exemplos Reais

Explicação da gravidade versus prioridade em testes de software: definições claras, uma matriz de quatro vias com exemplos, escalas de nível, quem d…

Leia o artigo →
Bancos de dados e SQL

Tutorial de subconsultas SQL: tipos, subconsultas correlacionadas e EXISTS com exemplos

Um tutorial de subconsultas SQL com consultas que executamos: subconsultas escalares, IN, FROM e SELECT, subconsultas correlacionadas, EXISTS, a arm…

Leia o artigo →
Bancos de dados e SQL

Transações SQL e ACID explicados: COMMIT, ROLLBACK e níveis de isolamento

Transações SQL e ACID explicadas com uma transferência bancária que executamos: COMMIT, ROLLBACK, savepoints, locks, níveis de isolamento, leituras …

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →