Classe em Python: O que É e Como Funciona | Python Brasil
Aprenda o que é classe em Python: atributos, métodos, herança, dunder methods, metaclasses, dataclasses e muito mais. Guia completo com exemplos.
O que é uma Classe?
Uma classe é um molde (ou blueprint) para criar objetos em Python. Ela define atributos (dados) e métodos (comportamentos) que os objetos criados a partir dela terão. Classes são a base da Programação Orientada a Objetos (POO) e, em Python, absolutamente tudo é um objeto — inclusive inteiros, strings e funções.
Entender classes em profundidade é fundamental para escrever código Python idiomático, legível e fácil de manter.
Atributos de classe vs atributos de instância
Um dos pontos mais importantes — e fonte de muitos bugs — é a diferença entre atributos de classe e atributos de instância.
class Contador:
total = 0 # Atributo de classe: compartilhado por TODAS as instâncias
def __init__(self, nome):
self.nome = nome # Atributo de instância: exclusivo de cada objeto
Contador.total += 1
c1 = Contador("c1")
c2 = Contador("c2")
print(Contador.total) # 2 — acessado pela classe
print(c1.total) # 2 — também acessado pela instância (leitura)
print(c1.nome) # c1 — exclusivo desta instância
# Cuidado: atribuir via instância cria um atributo LOCAL, não altera o de classe
c1.total = 99
print(c1.total) # 99 — atributo local da instância c1
print(Contador.total) # 2 — atributo de classe não foi alterado
Métodos de instância, de classe e estáticos
Python oferece três tipos de métodos, cada um com um papel distinto:
class Produto:
desconto_padrao = 0.10 # Atributo de classe
def __init__(self, nome, preco):
self.nome = nome
self.preco = preco
# Método de instância: recebe 'self', acessa atributos do objeto
def preco_com_desconto(self):
return self.preco * (1 - self.desconto_padrao)
# Método de classe: recebe 'cls', acessa/modifica a classe
@classmethod
def alterar_desconto(cls, novo_desconto):
cls.desconto_padrao = novo_desconto
# Método estático: não recebe 'self' nem 'cls', é apenas uma função relacionada
@staticmethod
def validar_preco(preco):
return preco > 0
p = Produto("Notebook", 3000)
print(p.preco_com_desconto()) # 2700.0
Produto.alterar_desconto(0.15)
print(p.preco_com_desconto()) # 2550.0
print(Produto.validar_preco(-1)) # False
Use @classmethod quando precisar de acesso à classe em si (por exemplo, fábricas alternativas). Use @staticmethod para utilitários relacionados à classe mas que não precisam acessar estado nenhum.
Métodos mágicos (dunder methods)
Os métodos que começam e terminam com dois underscores são chamados de dunder methods (de “double underscore”) ou métodos mágicos. Eles definem como seus objetos se comportam com operadores e funções nativas do Python.
class Vetor:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vetor({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
def __add__(self, outro):
return Vetor(self.x + outro.x, self.y + outro.y)
def __mul__(self, escalar):
return Vetor(self.x * escalar, self.y * escalar)
def __eq__(self, outro):
return self.x == outro.x and self.y == outro.y
def __len__(self):
return int((self.x**2 + self.y**2) ** 0.5)
def __bool__(self):
return self.x != 0 or self.y != 0
def __iter__(self):
yield self.x
yield self.y
v1 = Vetor(1, 2)
v2 = Vetor(3, 4)
print(v1 + v2) # (4, 6)
print(v1 * 3) # (3, 6)
print(v1 == v2) # False
print(len(v2)) # 5
print(list(v1)) # [1, 2]
Outros dunder methods importantes: __lt__, __le__, __gt__, __ge__ (comparações), __hash__ (uso em dicionários e sets), __contains__ (operador in), __getitem__ e __setitem__ (acesso por índice), __call__ (tornar objeto chamável como função), __enter__ e __exit__ (context managers).
Otimizando memória com slots
Por padrão, cada instância armazena seus atributos em um dicionário interno (__dict__), o que consome memória. Com __slots__, você define explicitamente quais atributos são permitidos, reduzindo o consumo de memória significativamente em classes com muitas instâncias.
class PontoSemSlots:
def __init__(self, x, y):
self.x = x
self.y = y
class PontoComSlots:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
# PontoComSlots usa menos memória e tem acesso de atributos mais rápido
p = PontoComSlots(1, 2)
# p.z = 3 # AttributeError — apenas x e y são permitidos
Classes abstratas com ABC
O módulo abc (Abstract Base Classes) permite definir interfaces que subclasses são obrigadas a implementar. Isso é útil para garantir um contrato entre a classe base e suas filhas.
from abc import ABC, abstractmethod
class Forma(ABC):
@abstractmethod
def area(self) -> float:
pass
@abstractmethod
def perimetro(self) -> float:
pass
def descricao(self):
return f"Área: {self.area():.2f}, Perímetro: {self.perimetro():.2f}"
class Circulo(Forma):
def __init__(self, raio):
self.raio = raio
def area(self):
return 3.14159 * self.raio ** 2
def perimetro(self):
return 2 * 3.14159 * self.raio
# Forma() # TypeError — não pode instanciar classe abstrata
c = Circulo(5)
print(c.descricao()) # Área: 78.54, Perímetro: 31.42
Dataclasses como alternativa moderna
Para classes que servem principalmente para armazenar dados, o decorator @dataclass gera automaticamente __init__, __repr__ e __eq__:
from dataclasses import dataclass
@dataclass
class Ponto:
x: float
y: float
def distancia_origem(self):
return (self.x**2 + self.y**2) ** 0.5
p = Ponto(3.0, 4.0)
print(p) # Ponto(x=3.0, y=4.0)
print(p.distancia_origem()) # 5.0
MRO: Ordem de Resolução de Métodos
Quando há herança múltipla, o Python usa o algoritmo C3 Linearization para determinar a ordem em que as classes são consultadas. Você pode inspecionar o MRO com .__mro__ ou mro():
class A:
def metodo(self): return "A"
class B(A):
def metodo(self): return "B"
class C(A):
def metodo(self): return "C"
class D(B, C):
pass
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
print(D().metodo()) # "B" — segue o MRO
Introdução a metaclasses
Toda classe em Python é uma instância de uma metaclasse — por padrão, type. As metaclasses controlam a criação das próprias classes e são usadas em frameworks avançados como o ORM do Django.
class MetaValidador(type):
def __new__(mcs, nome, bases, namespace):
for attr, valor in namespace.items():
if not attr.startswith('_') and callable(valor):
print(f"Método registrado: {attr}")
return super().__new__(mcs, nome, bases, namespace)
class MinhaClasse(metaclass=MetaValidador):
def metodo_a(self): pass
def metodo_b(self): pass
# Imprime: Método registrado: metodo_a
# Imprime: Método registrado: metodo_b
O protocolo descritor
Descritores são objetos que customizam o acesso a atributos via __get__, __set__ e __delete__. O decorator @property é implementado como um descritor internamente:
class Validado:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, f'_{self.name}', None)
def __set__(self, obj, value):
if not isinstance(value, (int, float)) or value < 0:
raise ValueError(f"{self.name} deve ser um número positivo")
setattr(obj, f'_{self.name}', value)
class Produto:
preco = Validado()
estoque = Validado()
def __init__(self, preco, estoque):
self.preco = preco
self.estoque = estoque
p = Produto(10.0, 5)
# p.preco = -1 # ValueError
Erros comuns
O erro mais frequente com atributos mutáveis de classe é usar listas ou dicionários como padrão:
# ERRADO — a lista é compartilhada entre todas as instâncias
class Turma:
alunos = [] # Atributo de classe mutável!
def adicionar(self, aluno):
self.alunos.append(aluno)
# CORRETO — lista criada por instância
class Turma:
def __init__(self):
self.alunos = [] # Atributo de instância
Quando usar classes?
Use classes quando você tem dados e comportamentos intimamente relacionados, quando precisa de múltiplas instâncias com estado próprio, ou quando quer aproveitar herança e polimorfismo. Para dados simples sem comportamento, considere dataclass ou namedtuple. Para funções puras sem estado, prefira módulos com funções.
Termos Relacionados
- Herança - Reutilização de código entre classes
- Polimorfismo - Diferentes formas para mesma interface
- Encapsulamento - Proteção de dados internos
- Dataclass - Forma simplificada de criar classes de dados