¿Hay alguna diferencia entre ==
y is
en Python?
Sí, tienen una diferencia muy importante.
==
: verifique la igualdad: la semántica es que los objetos equivalentes (que no son necesariamente el mismo objeto) probarán como iguales. Como dice la documentación :
Los operadores <,>, ==,> =, <=, y! = Comparan los valores de dos objetos.
is
: verifique la identidad: la semántica es que el objeto (como se mantiene en la memoria) es el objeto. De nuevo, la documentación dice :
Los operadores is
y la is not
prueba de identidad del objeto: x is y
es verdadero si y solo si x
y y
son el mismo objeto. La identidad del objeto se determina utilizando la id()
función. x is not y
produce el valor de verdad inverso.
Por lo tanto, verificar la identidad es lo mismo que verificar la igualdad de las ID de los objetos. Es decir,
a is b
es lo mismo que:
id(a) == id(b)
¿Dónde id
está la función incorporada que devuelve un número entero que "se garantiza que es único entre los objetos existentes simultáneamente" (ver help(id)
) y dónde a
y b
cualquier objeto arbitrario.
Otras direcciones de uso
Debe usar estas comparaciones para su semántica. Use is
para verificar la identidad y ==
para verificar la igualdad.
Entonces, en general, usamos is
para verificar la identidad. Esto suele ser útil cuando buscamos un objeto que solo debería existir una vez en la memoria, denominado "singleton" en la documentación.
Los casos de uso para is
incluyen:
None
- valores de enumeración (cuando se utilizan enumeraciones del módulo de enumeración)
- generalmente módulos
- generalmente objetos de clase resultantes de definiciones de clase
- generalmente objetos de función resultantes de definiciones de funciones
- cualquier otra cosa que solo debería existir una vez en la memoria (todos los singletons, generalmente)
- un objeto específico que desea por identidad
Los casos de uso habituales para ==
incluyen:
- números, incluyendo enteros
- instrumentos de cuerda
- liza
- conjuntos
- diccionarios
- objetos mutables personalizados
- otros objetos inmutables incorporados, en la mayoría de los casos
El caso de uso general, nuevamente, para ==
, es que el objeto que desea puede no ser el mismo objeto, sino que puede ser uno equivalente
PEP 8 direcciones
PEP 8, la guía de estilo oficial de Python para la biblioteca estándar también menciona dos casos de uso parais
:
Las comparaciones con singletons como None
siempre deben hacerse con is
o
is not
, nunca con los operadores de igualdad.
Además, tenga cuidado al escribir if x
cuando realmente quiere decir if x is not None
, por ejemplo, al probar si una variable o argumento predeterminado None
se estableció en algún otro valor. ¡El otro valor podría tener un tipo (como un contenedor) que podría ser falso en un contexto booleano!
Inferir igualdad de identidad
Si is
es cierto, la igualdad generalmente se puede inferir: lógicamente, si un objeto es él mismo, entonces debería probarse como equivalente a sí mismo.
En la mayoría de los casos, esta lógica es cierta, pero se basa en la implementación del __eq__
método especial. Como dicen los documentos ,
El comportamiento predeterminado para la comparación de igualdad ( ==
y !=
) se basa en la identidad de los objetos. Por lo tanto, la comparación de igualdad de instancias con la misma identidad resulta en igualdad, y la comparación de igualdad de instancias con identidades diferentes resulta en desigualdad. Una motivación para este comportamiento predeterminado es el deseo de que todos los objetos sean reflexivos (es decir, x es y implica x == y).
y en aras de la coherencia, recomienda:
La comparación de igualdad debe ser reflexiva. En otras palabras, los objetos idénticos deben comparar iguales:
x is y
implica x == y
Podemos ver que este es el comportamiento predeterminado para los objetos personalizados:
>>> class Object(object): pass
>>> obj = Object()
>>> obj2 = Object()
>>> obj == obj, obj is obj
(True, True)
>>> obj == obj2, obj is obj2
(False, False)
El contrapositivo también suele ser cierto: si algo prueba que no es igual, generalmente se puede inferir que no son el mismo objeto.
Dado que las pruebas de igualdad se pueden personalizar, esta inferencia no siempre es válida para todos los tipos.
Una excepción
Una excepción notable es nan
: siempre prueba que no es igual a sí mismo:
>>> nan = float('nan')
>>> nan
nan
>>> nan is nan
True
>>> nan == nan # !!!!!
False
Verificar la identidad puede ser una verificación mucho más rápida que verificar la igualdad (lo que puede requerir la verificación recursiva de los miembros).
Pero no puede ser sustituido por igualdad donde puede encontrar más de un objeto como equivalente.
Tenga en cuenta que al comparar la igualdad de listas y tuplas se supondrá que la identidad de los objetos es igual (porque esta es una comprobación rápida). Esto puede crear contradicciones si la lógica es inconsistente, como lo es para nan
:
>>> [nan] == [nan]
True
>>> (nan,) == (nan,)
True
Un cuento de precaución:
La pregunta está intentando usar is
para comparar enteros. No debe suponer que una instancia de un entero es la misma instancia que una obtenida por otra referencia. Esta historia explica por qué.
Un comentarista tenía un código que se basaba en el hecho de que los enteros pequeños (-5 a 256 inclusive) son singletons en Python, en lugar de verificar la igualdad.
Wow, esto puede conducir a algunos errores insidiosos. Tenía un código que verificaba si a es b, que funcionó como quería porque a y b suelen ser números pequeños. El error solo ocurrió hoy, después de seis meses de producción, porque a y b finalmente fueron lo suficientemente grandes como para no ser almacenados en caché. - gwg
Funcionó en desarrollo. Puede haber pasado algunas pruebas unitarias.
Y funcionó en producción, hasta que el código verificó un número entero mayor que 256, momento en el cual falló en producción.
Esta es una falla de producción que podría haberse detectado en la revisión del código o posiblemente con un verificador de estilo.
Permítanme enfatizar: no lo use is
para comparar enteros.
echo 'import sys;tt=sys.argv[1];print(tt is "foo", tt == "foo", id(tt)==id("foo"))'| python3 - foo
salida:False True False
.