---
title: "Classe em Python: O que É e Como Funciona | Python Brasil"
url: "https://python.dev.br/glossario/classe/"
markdown_url: "https://python.dev.br/glossario/classe.MD"
description: "Aprenda o que é classe em Python: atributos, métodos, herança, dunder methods, metaclasses, dataclasses e muito mais. Guia completo com exemplos."
date: "2025-09-18"
author: ""
---

# 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.

```python
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:

```python
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.

```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.

```python
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.

```python
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__`:

```python
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()`:

```python
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.

```python
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:

```python
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:

```python
# 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](/glossario/heranca/) - Reutilização de código entre classes
- [Polimorfismo](/glossario/polimorfismo/) - Diferentes formas para mesma interface
- [Encapsulamento](/glossario/encapsulamento/) - Proteção de dados internos
- [Dataclass](/glossario/dataclass/) - Forma simplificada de criar classes de dados
