Cuando se empieza en Python es bastante frecuente usar como sinónimos los comparadores is y ==. Aunque, en general, suelen funcionar de forma similar sus comportamientos no son exactamente iguales.
is devolverá True si las dos variables apuntan al mismo objeto.
== devolverá True si los valores de las variables son iguales.
Para acabar de entenderlo veamos algunos ejemplos:
// creamos una lista
>>> a = [1, 2 , 3, ]
// la referenciamos en otra variable
>>> b = a
// ¿Se refieren ambas variables al mismo objeto?
>>> b is a
True
// ¿Son iguales?
>>> b == a
True
// Ahora vamos a generar una copia de la lista
>>> b = a[:]
// ¿Se refieren al mismo objeto?
>>> b is a
False
// ¿Son iguales?
>>> b == a
True
Creo que este ejemplo es muy descriptivo pero veamos unos ejemplos más complejos:
// si creamos 2 listas iguales...
// ¿Son el mismo objeto?
>>> [1,2] is [1,2]
False
// En Python todo son objetos entonces, ¿por qué este comportamiento?
>>> 2 is 2
True
// Esto sucede porque Python pone los pequeños enteros en caché.
// ¿Y si creamos 2 enteros más grandes?
>>> 1000 is 1000
True
// Pero en cambio:
>>> 1000 is 999 + 1
False
Lo mismo es aplicable a los caracteres y cadenas:
>>> "a" is "a"
True
>>> "aa" is "a" * 2
True
// En cambio...
>>> a = "a"
>>> "aa" is a*2
False
Como véis no es difícil de entender pero hay que ser muy cuidadoso. Aunque ambos son útiles, is hace que nuestro código sea muy legible pero puede resultar muy engañoso. ¡Extremad las precauciones!