Variáveis: o que são e como o Python as gerencia na memória
1. O que é uma variável em Python?
Em Python, uma variável é essencialmente um nome que referencia um objeto na memória. Diferente de linguagens como C ou C++, onde a variável é uma "caixa" que armazena um valor diretamente, em Python a variável funciona como um rótulo ou etiqueta que aponta para um objeto.
Quando escrevemos:
x = 42
O Python realiza duas operações:
1. Cria um objeto int com valor 42 no heap (memória dinâmica)
2. Associa o nome x a esse objeto
Isso significa que a variável não contém o valor 42 — ela apenas "aponta" para o objeto que contém esse valor. Essa distinção é fundamental para entender como Python gerencia memória.
2. Atribuição e reatribuição de variáveis
O mecanismo de atribuição em Python é chamado de binding (vinculação). Quando fazemos:
nome = "Maria"
idade = 30
Criamos dois objetos (str e int) e vinculamos os nomes a eles. A grande flexibilidade de Python é que uma variável pode referenciar objetos de tipos diferentes ao longo do programa:
x = 10 # x aponta para um int
print(type(x)) # <class 'int'>
x = "texto" # x agora aponta para uma string
print(type(x)) # <class 'str'>
Isso ocorre porque variáveis não possuem tipo fixo — o tipo pertence ao objeto, não ao nome que o referencia.
3. Gerenciamento de memória: alocação e referências
Python gerencia a memória automaticamente. Objetos são alocados dinamicamente no heap, e cada objeto mantém um contador de referências que rastreia quantos nomes (ou outras estruturas) apontam para ele.
a = [1, 2, 3] # Cria uma lista; contagem de referências = 1
b = a # b aponta para o mesmo objeto; contagem = 2
c = a # c também aponta; contagem = 3
Podemos verificar a identidade do objeto com id() e a contagem de referências com sys.getrefcount():
import sys
x = [10, 20]
print(sys.getrefcount(x)) # Contagem inclui a referência temporária da função
4. Coleta de lixo (Garbage Collection)
Quando a contagem de referências de um objeto chega a zero, Python o desaloca automaticamente. Esse é o mecanismo principal de gerenciamento de memória.
def exemplo():
temp = [1, 2, 3] # Objeto criado
print(temp)
# Ao sair da função, 'temp' é destruído
# Contagem de referências chega a zero → objeto desalocado
exemplo()
No entanto, existe um problema: referências circulares. Duas listas que se referenciam mutuamente nunca terão contagem zero, mesmo que não sejam mais acessíveis:
a = []
b = []
a.append(b) # a → b
b.append(a) # b → a (referência circular)
Python resolve isso com um coletor de lixo cíclico, implementado no módulo gc. Podemos forçar a coleta manualmente:
import gc
gc.collect() # Força a coleta de objetos inacessíveis
5. Mutabilidade e imutabilidade: impacto na memória
Objetos em Python podem ser mutáveis ou imutáveis, e isso afeta diretamente o uso de memória.
Objetos imutáveis (int, str, tuple, frozenset): qualquer operação que pareça modificar o objeto na verdade cria um novo objeto:
x = "Python"
print(id(x)) # Endereço de memória
x = x + " 3"
print(id(x)) # Novo endereço — um novo objeto foi criado
Objetos mutáveis (list, dict, set, bytearray): podem ser alterados sem mudar de identidade:
lista = [1, 2, 3]
print(id(lista)) # Endereço original
lista.append(4)
print(id(lista)) # Mesmo endereço — objeto foi modificado in-place
Essa diferença é crucial para performance: modificar uma lista grande é mais eficiente que "modificar" uma tupla grande.
6. Compartilhamento de objetos e o operador is
Variáveis diferentes podem apontar para o mesmo objeto — isso é chamado de aliasing:
a = [1, 2, 3]
b = a # b é um alias de a
b.append(4)
print(a) # [1, 2, 3, 4] — a também foi modificado!
Para diferenciar entre igualdade de valor e identidade de objeto, usamos:
==: compara se os valores são iguaisis: compara se são o mesmo objeto na memória
x = [1, 2]
y = [1, 2]
print(x == y) # True (mesmo valor)
print(x is y) # False (objetos diferentes)
Python otimiza memória com interning para inteiros pequenos (-5 a 256) e strings curtas:
a = 256
b = 256
print(a is b) # True (interning)
c = 257
d = 257
print(c is d) # False (cada um é um objeto separado)
7. Escopo de variáveis e a tabela de símbolos
Python segue a regra LEGB para resolução de nomes:
- Local: escopo da função atual
- Enclosing: escopo de funções externas (closures)
- Global: escopo do módulo
- Built-in: nomes embutidos como
print,len
Cada escopo mantém uma tabela de símbolos (um dicionário) que mapeia nomes a objetos:
x = 10 # Variável global
def funcao():
y = 20 # Variável local
print(locals()) # {'y': 20}
funcao()
print(globals()['x']) # 10 — acessando pela tabela global
Variáveis locais são criadas na entrada da função e descartadas na saída (a menos que sejam capturadas por closures):
def contador():
count = 0
def incrementar():
nonlocal count
count += 1
return count
return incrementar
c = contador()
print(c()) # 1
print(c()) # 2
# 'count' é preservado pelo closure
Referências
- Python Documentation: Data Model — Documentação oficial sobre objetos, valores e tipos em Python
- Python Documentation: Garbage Collector — Documentação do módulo
gcpara controle do coletor de lixo - Real Python: Variables in Python — Tutorial completo sobre variáveis, escopo e gerenciamento de memória
- Python Memory Management (GeeksforGeeks) — Artigo técnico detalhado sobre alocação de memória e contagem de referências
- Understanding Python's Memory Model (Toptal) — Explicação aprofundada do modelo de memória Python, incluindo interning e garbage collection
- Python's
isvs==: Everything You Need to Know — Guia prático sobre a diferença entre identidade e igualdade em Python