Atributos Python: entendendo como funcionam 🐍

Atributos são a base do modelo de objetos do Python, mas a maioria dos iniciantes (e até muitos plenos) os usam intuitivamente, sem entender o que acontece "debaixo do capô". O resultado: comportamento imprevisível do código, consumo de memória e APIs espaguete.

Vamos entender os detalhes 👇

1️⃣ Ordem de busca (MRO)
Python vive pelo princípio: primeiro o objeto, depois a classe.

Quando você acessa obj.attr, o interpretador primeiro olha em obj.__dict__. Se não encontrar, vai para Class.__dict__.
Se você acidentalmente escrever algo em obj.attr, criará um atributo de instância que ocultará o atributo de classe.

class Hero:
weapon = "Sword"

h = Hero()
h.weapon = "Gun" # Agora h tem sua própria arma, a classe ainda tem a espada

Isso não é um bug, é uma feature, mas controle isso. Se quiser alterar o atributo para todos — mexa na classe.

2️⃣ __dict__ vs __slots__
Por padrão, cada objeto tem __dict__ — uma tabela hash (dicionário) onde os atributos são armazenados. Isso é flexível, mas caro em memória.

Se você cria milhões de objetos do mesmo tipo, use __slots__.
🔵O que isso faz: Impede a criação de __dict__ para a instância, fixando rigidamente o conjunto de atributos na memória (como um array).
🔵Benefício: Redução de 25-30% no consumo de memória e acesso um pouco mais rápido.
🔵Custo: Você perde a capacidade dinâmica de adicionar novos atributos em tempo de execução.

3️⃣ A ilusão de encapsulamento
Em Python não existem campos "privados". De jeito nenhum.
Construções como __attribute não são proteção de dados, mas sim name mangling (distorção de nomes). Python simplesmente renomeia a variável para _ClassName__attribute para evitar sobrescrita acidental em subclasses.
Se você usa __ esperando que ninguém acesse seus dados, qualquer um que saiba usar dir() verá tudo. Usar _private (um sublinhado) significa "não mexa, é uma API interna". É uma questão de cultura, não de compilador.

Quer ocultar dados? Use um sublinhado inferior _ apenas como sinal para colegas: "Não mexa nisso, é parte interna".

4️⃣ @property: o getter ético
Nunca faça getters no estilo Java em Python (get_value(), set_value()). Se precisar de lógica ao acessar um atributo, use @property.
@property permite transformar um método em um atributo. Você mantém uma API limpa (acesso por ponto), mas por baixo dos panos pode validar dados, registrar acesso ou calcular valores em tempo real.
class DataStorage:
def __init__(self, value):
self._value = value

@property
def value(self):
return self._value

@value.setter
def value(self, new_val):
if not isinstance(new_val, int):
raise ValueError("Só int, querido") self._value = new_val


Assim você mantém uma interface limpa: obj.value = 10 parece tão simples quanto trabalhar com um campo comum, mas você controla o que é realmente escrito.

Que outros lugares "mágicos" do Python geram dúvidas em vocês? Mandem nos comentários, vamos analisar. 👇

#anatomia_do_python