Operadores de comparação e identidade: == vs is

1. Introdução aos operadores de comparação em Python

Operadores de comparação são fundamentais na lógica de programação, permitindo que tomemos decisões baseadas em relações entre valores. Python oferece um conjunto completo desses operadores: == (igual), != (diferente), < (menor), > (maior), <= (menor ou igual) e >= (maior ou igual).

No entanto, dois operadores frequentemente causam confusão entre iniciantes e até mesmo desenvolvedores experientes: == e is. Embora ambos pareçam verificar "igualdade", eles operam em níveis completamente diferentes. O operador == compara valores, enquanto is compara identidade de objetos. Compreender essa distinção é crucial para escrever código Python correto e eficiente.

2. O operador ==: comparando valores

O operador == verifica se dois objetos possuem o mesmo valor ou conteúdo. Ele não se importa se são objetos diferentes na memória; apenas avalia se seus dados são equivalentes.

# Comparações simples com ==
print(5 == 5)           # True: mesmo valor numérico
print("python" == "python")  # True: mesmo conteúdo textual
print([1, 2] == [1, 2]) # True: listas com mesmos elementos

# Comparação entre tipos diferentes
print(5 == 5.0)         # True: Python converte automaticamente
print("5" == 5)         # False: string vs int

Um caso clássico que demonstra as limitações de == com floats:

# Problema de precisão com floats
print(0.1 + 0.2 == 0.3)  # False! Surpreendente, não?
print(0.1 + 0.2)         # 0.30000000000000004

Isso ocorre porque números de ponto flutuante são representados em binário, e algumas frações decimais não podem ser representadas exatamente. Para comparações precisas com floats, use math.isclose().

3. O operador is: comparando identidade de objetos

O operador is verifica se duas variáveis referenciam exatamente o mesmo objeto na memória. Cada objeto em Python possui um identificador único, acessível via id().

# Entendendo identidade de objetos
a = [1, 2]
b = a          # b aponta para o mesmo objeto que a
c = [1, 2]     # c é um novo objeto com mesmo conteúdo

print(a is b)  # True: mesmo objeto
print(a is c)  # False: objetos diferentes
print(id(a), id(b), id(c))  # a e b compartilham o mesmo id

Note que a is [1, 2] sempre retorna False, pois [1, 2] cria um novo objeto a cada execução.

4. Quando == e is concordam (e quando divergem)

Casos de concordância

Python aplica uma otimização chamada interning para alguns objetos imutáveis. Isso faz com que objetos com o mesmo valor compartilhem a mesma identidade em certos casos:

# Interning de inteiros pequenos
a = 5
b = 5
print(a == b)  # True
print(a is b)  # True (devido ao interning)

# Interning de strings simples
x = "hello"
y = "hello"
print(x == y)  # True
print(x is y)  # True (strings curtas são internadas)

Casos de divergência

Para objetos mutáveis como listas, dicionários e conjuntos, == e is frequentemente divergem:

# Divergência clássica com listas
a = [1, 2]
b = [1, 2]

print(a == b)  # True: mesmo conteúdo
print(a is b)  # False: objetos diferentes na memória

# Com dicionários
d1 = {"nome": "Ana", "idade": 30}
d2 = {"nome": "Ana", "idade": 30}
print(d1 == d2)  # True
print(d1 is d2)  # False

5. O fenômeno do interning em Python

Interning é uma técnica de otimização onde Python reutiliza objetos imutáveis idênticos para economizar memória. Isso é mais visível com inteiros pequenos e strings simples.

# Interning de inteiros: limite visível
a = 256
b = 256
print(a is b)  # True (dentro da faixa -5 a 256)

c = 257
d = 257
print(c is d)  # False (fora da faixa de interning)

# Interning de strings: depende do conteúdo
s1 = "Python"
s2 = "Python"
print(s1 is s2)  # True (strings alfanuméricas curtas)

s3 = "Python " * 100  # String longa
s4 = "Python " * 100
print(s3 is s4)  # False (muito longa para interning automático)

Importante: Nunca confie no interning para lógica de programa. Sempre use == para comparar valores e is apenas para identidade.

6. Boas práticas: quando usar == e quando usar is

Regras práticas:

  1. Use == para comparar valores: números, strings, listas, dicionários, etc.

  2. Use is para comparar com singletons: None, True, False.

# Correto: comparando com None
def processar_dados(dados):
    if dados is None:  # Padrão recomendado
        return "Sem dados"
    return f"Dados processados: {dados}"

# Evite: == None funciona, mas é menos idiomático
if dados == None:  # Funciona, mas não é recomendado
    pass

# Comparação com booleanos
flag = True
if flag is True:   # Explícito e claro
    print("Ativo")
  1. Cuidado com objetos mutáveis: Sempre use == para verificar igualdade de conteúdo.
# Armadilha comum
lista_a = [1, 2, 3]
lista_b = [1, 2, 3]

if lista_a == lista_b:  # Correto: comparando conteúdo
    print("Mesmos valores")

if lista_a is lista_b:  # Quase sempre False para listas diferentes
    print("Mesmo objeto")  # Raramente executado
  1. Para enums e objetos personalizados: Use is para comparar membros de enumeração e == para comparar valores de atributos.

7. O método __eq__: personalizando a comparação com ==

Podemos definir como == se comporta para nossas próprias classes sobrescrevendo o método __eq__.

class Pessoa:
    def __init__(self, nome, cpf):
        self.nome = nome
        self.cpf = cpf

    def __eq__(self, other):
        if not isinstance(other, Pessoa):
            return NotImplemented
        # Duas pessoas são "iguais" se tiverem o mesmo CPF
        return self.cpf == other.cpf

    def __hash__(self):
        # Importante: objetos iguais devem ter o mesmo hash
        return hash(self.cpf)

# Testando
p1 = Pessoa("Ana", "123.456.789-00")
p2 = Pessoa("Ana Silva", "123.456.789-00")  # Mesmo CPF, nome diferente
p3 = Pessoa("Carlos", "987.654.321-00")

print(p1 == p2)  # True (mesmo CPF)
print(p1 == p3)  # False (CPFs diferentes)
print(p1 is p2)  # False (objetos diferentes)

# Uso em conjunto (depende de __hash__)
conjunto = {p1, p2, p3}
print(len(conjunto))  # 2 (p1 e p2 são considerados iguais)

8. Resumo e dicas finais

Operador Compara Exemplo típico Uso recomendado
== Valor/conteúdo a == b Dados em geral
is Identidade (memória) a is None Singletons

Checklist para evitar erros comuns:

  • [ ] Para comparar números, strings ou coleções → use ==
  • [ ] Para verificar se uma variável é None → use is None
  • [ ] Para verificar booleanos → prefira if variavel: em vez de if variavel is True:
  • [ ] Nunca confie em interning para lógica de programa
  • [ ] Ao criar classes, implemente __eq__ e __hash__ juntos se for usar a classe em conjuntos ou dicionários

Exercício prático:

Analise cada comparação e decida se deve usar == ou is:

# 1. Verificar se duas strings têm o mesmo texto
texto1 = "Python"
texto2 = "python".upper()
# Resposta: texto1 == texto2

# 2. Verificar se uma variável não tem valor atribuído
var = None
# Resposta: var is None

# 3. Verificar se duas listas têm os mesmos elementos
lista1 = [1, 2, 3]
lista2 = [1, 2, 3]
# Resposta: lista1 == lista2

# 4. Verificar se duas variáveis apontam para o mesmo objeto
a = [1, 2]
b = a
# Resposta: a is b

Lembre-se: == pergunta "os valores são equivalentes?", enquanto is pergunta "é exatamente o mesmo objeto?". Dominar essa diferença é um marco no aprendizado de Python.

Referências