Entender como o Python acessa e gerencia variáveis associadas a classes e objetos é fundamental. Este guia detalha o processo de busca de atributos e os princípios por trás do compartilhamento de dados.
Conceitos Fundamentais: Classe vs. Instância
Uma classe define o molde, o projeto que estabelece a estrutura e comportamentos. Uma instância é um objeto concreto criado a partir desse molde.
- Variável de Classe: Definida no corpo da classe, fora de qualquer método. Pertence à classe e é compartilhada por todas as suas instâncias. Geralmente usada para constantes ou dados comuns a todos os objetos.
- Variável de Instância: Normalmente definida dentro do método
__init__usandoself. Cada instância mantém sua própria cópia independente. Serve para armazenar dados únicos de cada objeto.
Sequência de Busca de Atributos
Ao acessar objeto.atributo, o inteprretador segue uma ordem específica, conhecida como cadeia de resolução de métodos (MRO) simplificada para atributos.
Acessando um Atributo
- Busca na Instância: Verifica primeiro o dicionário de atributos da instância (
objeto.__dict__). - Busca na Classe: Se não encontrado, busca no dicionário da classe (
Classe.__dict__). - Busca nas Classes Mãe: Continua subindo na hierarquia de herança até encontrar ou lançar um
AttributeError.
Atribuindo um Valor (objeto.atributo = valor)
O comportamento depende da intenção:
Cenário 1: Criar/Modificar uma Variável de Instância (Ação mais comum)
def __init__(self, modelo):
self.modelo = modelo # Variável de instancia
carro_a = Automovel("Sedan") carro_b = Automovel("SUV")
Ambos acessam a variável de classe inicialmente
print(carro_a.rodas) # Saída: 4 print(carro_b.rodas) # Saída: 4
Atribuição via instância
carro_a.rodas = 6 # Cria uma variável de INSTÂNCIA 'rodas' em carro_a
print(carro_a.rodas) # Saída: 6 (encontra na instância) print(carro_b.rodas) # Saída: 4 (busca na classe) print(Automovel.rodas) # Saída: 4 (acesso direto à classe)
print(carro_a.dict) # {'modelo': 'Sedan', 'rodas': 6}
</div>**Explicação:** A operação `carro_a.rodas = 6` **não** altera a variável de classe. Em vez disso, ela **cria uma nova variável** com o mesmo nome (`rodas`) no espaço de nomes da instância `carro_a`. Esse fenômeno é chamado de **sombreamento de atributo**.
**Cenário 2: Modificar a Variável de Classe através da Instância** (Geralmente não recomendado)
<div class="code-example">```
# Continuando o exemplo anterior
carro_a.__class__.rodas = 8 # Acessa a classe via __class__ e modifica
print(carro_a.rodas) # Saída: 6 (sua própria variável de instância)
print(carro_b.rodas) # Saída: 8 (buscou na classe, que agora vale 8)
print(Automovel.rodas) # Saída: 8
Princípio de Implementação: Dicionários e Espaços de Nomes
Internamente, o Python armazena atributos em estruturas de dicionário.
- Espaço de Nomes da Instância: É representado pelo
objeto.__dict__. Quando você escreveself.cor = "vermelho", está fazendoself.__dict__['cor'] = "vermelho". - Espaço de Nomes da Classe: É representado por
Classe.__dict__, contendo variáveis de classe, métodos e outros atributos definidos no nível da classe.
Um espaço de nomes (namespace) é um mapeamento de nomes para objetos. A sequência de busca por atributos segue esta ordem: Espaço da Instância → Espaço da Classe → Espaço das Classses Mãe.
Armadilhas Comuns e Boas Práticas
Atrapalhe Clássica: Objetos Mutáveis como Variáveis de Classe
produto_1 = Inventario() produto_2 = Inventario()
produto_1.itens.append("Parafuso")
Ambas as instâncias e a classe veem a alteração
print(produto_1.itens) # ['Parafuso'] print(produto_2.itens) # ['Parafuso'] print(Inventario.itens) # ['Parafuso']
</div>**Solução:** Para dados mutáveis específicos de cada instância, inicialize-os no `__init__`.
<div class="code-example">```
class InventarioCorreto:
def __init__(self):
self.itens = [] # Cada instância recebe sua própria lista
item_1 = InventarioCorreto()
item_2 = InventarioCorreto()
item_1.itens.append("Arruela")
print(item_1.itens) # ['Arruela']
print(item_2.itens) # []
- Separação Clara: Defina variáveis de classe no topo da definição da classe e variáveis de instância dentro do
__init__. - Modificação Intencional: Use
Classe.atributopara alterar variáveis de classe. Eviteinstância.atributopara esse propósito. - Cautela com Dados Compartilhados: Prefira inicializar estruturas mutáveis como listas ou dicionários dentro de
__init__, a menos que o compartilhamento seja o objetivo.