Comprender el operador "es" de Python

110

El isoperador no coincide con los valores de las variables, sino con las propias instancias.

que significa realmente?

Declaro dos variables nombradas xy yasignando los mismos valores en ambas variables, pero devuelve falso cuando uso el isoperador.

Necesito una aclaración. Aquí está mi código.

x = [1, 2, 3]
y = [1, 2, 3]

print(x is y)  # It prints false!
aniskhan001
fuente
Pregunta relacionada stackoverflow.com/questions/38189660/…
Kasravnd

Respuestas:

181

No entendiste lo isque prueba el operador. Prueba si dos variables apuntan al mismo objeto , no si dos variables tienen el mismo valor.

De la documentación para el isoperador :

Los operadores isy la is notprueba de identidad del objeto: x is yes verdadero si y solo si xy yson el mismo objeto.

Utilice el ==operador en su lugar:

print(x == y)

Esto imprime True. xy yson dos listas separadas :

x[0] = 4
print(y)  # prints [1, 2, 3]
print(x == y)   # prints False

Si usa la id()función , verá eso xy ytendrá diferentes identificadores:

>>> id(x)
4401064560
>>> id(y)
4401098192

pero si se va a asignar ya xcontinuación, ambos apuntan al mismo objeto:

>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True

y ismuestra que ambos son el mismo objeto, regresa True.

Recuerde que en Python, los nombres son solo etiquetas que hacen referencia a valores ; puede hacer que varios nombres apunten al mismo objeto. isle dice si dos nombres apuntan a un mismo objeto. ==le dice si dos nombres se refieren a objetos que tienen el mismo valor.

Martijn Pieters
fuente
13
Entonces, A is Bes lo mismo que id(A) == id(B).
imallett
2
@imallett: es un proxy para la misma prueba, siempre que no almacene id(A)en una variable y luego espere variable == id(B)seguir funcionando; si Ase eliminó mientras tanto, Bpodría haber recibido la misma ubicación de memoria.
Martijn Pieters
1
No se pudo formatear en los comentarios. Pero hay algo interesante. :) >>> x = 5 \n>>> y = 5 \n>>> x es y \nVerdadero \n>>> x == y \nVerdadero \n>>>\n
Haranadh
5
Los enteros pequeños se internan en CPython ya que se usan con mucha frecuencia. Es una optimización. x = 5; y = 5; x es y => Verdadero porque id (x) == id (y). Es el mismo objeto entero que se reutiliza. Funciona en Python ya que los enteros son inmutables. Si hace x = 1.0; y = 1.0 o x = 9999; y = 9999, no será la misma identidad, porque los flotantes y los ints más grandes no están internos.
Magnus Lyckå
1
@ MagnusLyckå hay otras optimizaciones que pueden hacer que los objetos inmutables se almacenen en caché. Por ejemplo, si ejecuta su ejemplo en una nueva función o junto con un punto y coma de separación en el intérprete interactivo, encontrará que también tienen el mismo ID.
Martijn Pieters
60

Otro duplicado preguntaba por qué dos cadenas iguales generalmente no son idénticas, lo que realmente no se responde aquí:

>>> x = 'a' 
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False

Entonces, ¿por qué no son la misma cadena? Especialmente dado esto:

>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True

Dejemos la segunda parte por un momento. ¿Cómo podría ser cierto el primero?

El intérprete tendría que tener una "tabla interna", una tabla que mapee valores de cadena a objetos de cadena, por lo que cada vez que intente crear una nueva cadena con el contenido 'abc', obtendrá el mismo objeto. Wikipedia tiene una discusión más detallada sobre cómo funciona la pasantía.

Y Python tiene una mesa de prácticas de cadenas; puede internar cadenas manualmente con el sys.internmétodo.

De hecho, Python puede internar automáticamente cualquier tipo inmutable, pero no está obligado a hacerlo. Diferentes implementaciones incorporarán diferentes valores.

CPython (la implementación que estás usando si no sabes qué implementación estás usando) auto-interna enteros pequeños y algunos singleton especiales como False, pero no cadenas (o enteros grandes, o tuplas pequeñas, o cualquier otra cosa). Puedes ver esto con bastante facilidad:

>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False

OK, pero ¿por qué eran zy weran idénticos?

Ese no es el intérprete que interviene automáticamente, son los valores de plegado del compilador.

Si la misma cadena en tiempo de compilación aparece dos veces en el mismo módulo (lo que exactamente significa esto es difícil de definir, que no es lo mismo que un literal de cadena, porque r'abc', 'abc'y 'a' 'b' 'c'son todas diferentes literales, pero la misma cadena, pero fácil de entender intuitivamente), el compilador solo creará una instancia de la cadena, con dos referencias.

De hecho, el compilador puede ir aún más lejos: el optimizador 'ab' + 'c'puede convertirlo a 'abc', en cuyo caso se puede plegar junto con una 'abc'constante en el mismo módulo.

Nuevamente, esto es algo que Python está permitido pero no está obligado a hacer. Pero en este caso, CPython siempre pliega cadenas pequeñas (y también, por ejemplo, tuplas pequeñas). (Aunque el compilador declaración por declaración del intérprete interactivo no ejecuta la misma optimización que el compilador módulo a la vez, no verá exactamente los mismos resultados de forma interactiva).


Entonces, ¿qué debe hacer al respecto como programador?

Pues nada. Casi nunca tienes motivos para preocuparte si dos valores inmutables son idénticos. Si desea saber cuándo puede usar en a is blugar de a == b, está haciendo la pregunta incorrecta. Solo utilícelo siempre, a == bexcepto en dos casos:

  • Para comparaciones más legibles con los valores singleton como x is None.
  • Para valores mutables, cuando necesite saber si la mutación xafectará al y.
abarnert
fuente
1
Excelente explicación, especialmente tu consejo al final.
DavidG
Gracias por esa explicación detallada. Alguien sabe: si wy zson idénticos debido a los valores de plegado del compilador, ¿por qué esto también funciona en el REPL, incluso usando id()para verificar las referencias? Uso de REPL en Python 3.7
Chi-chi
8

issolo devuelve verdadero si en realidad son el mismo objeto. Si fueran iguales, también aparecería un cambio en uno en el otro. He aquí un ejemplo de la diferencia.

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]
Mark Ransom
fuente
8

A raíz de una pregunta duplicada , esta analogía podría funcionar:

# - Darling, I want some pudding!
# - There is some in the fridge.

pudding_to_eat = fridge_pudding
pudding_to_eat is fridge_pudding
# => True

# - Honey, what's with all the dirty dishes?
# - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
# - But there was already some in the fridge.

pudding_to_eat = make_pudding(ingredients)
pudding_to_eat is fridge_pudding
# => False
Amadan
fuente
3
Podría ser solo gusto personal (sin juego de palabras) pero encontré esta analogía más confusa que útil y me hizo querer comer pudín cuando no tengo ninguno en mi refrigerador :( Creo que la respuesta de Mark Ransom, aunque más aburrida, es probablemente más instructivo
Tom Close
1
@TomClose: Hay muchas buenas respuestas a esta pregunta, suficientes para que haya espacio para la ligereza. Además, yo también quiero pudín.
Amadan
5

isy is notson los dos operadores de identidad en Python. isEl operador no compara los valores de las variables, sino que compara las identidades de las variables. Considera esto:

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>

El ejemplo anterior muestra que la identidad (también puede ser la dirección de memoria en Cpython) es diferente para ambos ay b(aunque sus valores son los mismos). Es por eso que cuando dice a is bque devuelve falso debido a la falta de coincidencia en las identidades de ambos operandos. Sin embargo, cuando dice a == b, devuelve verdadero porque la ==operación solo verifica si ambos operandos tienen el mismo valor asignado.

Ejemplo interesante (para la nota extra):

>>> del a
>>> del b
>>> a = 132
>>> b = 132
>>> hex(id(a))
'0x7faa2b609738'
>>> hex(id(b))
'0x7faa2b609738'
>>> a is b
True
>>> a == b
True
>>>

En el ejemplo anterior, aunque ay bson dos variables diferentes, se a is bdevolvió True. Esto se debe a que el tipo de aes intque es un objeto inmutable. Entonces, Python (supongo que para ahorrar memoria) asignó el mismo objeto a bcuando se creó con el mismo valor. Entonces, en este caso, las identidades de las variables coincidieron y a is bresultaron ser True.

Esto se aplicará a todos los objetos inmutables:

>>> del a
>>> del b
>>> a = "asd"
>>> b = "asd"
>>> hex(id(a))
'0x1079b05a8'
>>> hex(id(b))
'0x1079b05a8'
>>> a is b
True
>>> a == b
True
>>>

Espero que ayude.

gixxer
fuente
este es un buen ejemplo. gracias por la información detallada.
Haranadh
Pero intente a = 123456789 b = 123456789
user2183078
Todo lo que sea menor -5o mayor que 256en Python será Falso. Python almacena en caché números en el rango [-5, 256].
inteligente
No todos los objetos inmutables se compartirán como muestra, esa es una optimización aplicada por el tiempo de ejecución de Python para algunos objetos pero no para otros. El proceso de compartir números enteros pequeños está bien documentado, pero no creo que sea para prácticas de cadenas .
Mark Ransom
4

x is yes lo mismo que id(x) == id(y)comparar la identidad de los objetos.

Como @ tomasz-kurgan señaló en el comentario a continuación, el isoperador se comporta de manera inusual con ciertos objetos.

P.ej

>>> class A(object):
...   def foo(self):
...     pass
... 
>>> a = A()
>>> a.foo is a.foo
False
>>> id(a.foo) == id(a.foo)
True

Árbitro;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24

Nizam Mohamed
fuente
No, no es así. Puede comportarse de manera similar en la mayoría de los casos, pero no siempre es cierto. Vea esto : al final de la página, viñeta 6 .:> (...), puede notar un comportamiento aparentemente inusual en ciertos usos del operador is , como los que involucran comparaciones entre métodos de instancia o constantes Y el ejemplo de trabajo mínimo : `class A (objeto): def foo (self): pass a = A () print a.foo is a.foo print id (a.foo) == id (a.foo)`
Tomasz Kurgan
3

Como puede comprobar aquí a pequeños enteros. Los números por encima de 257 no son entradas pequeñas, por lo que se calcula como un objeto diferente.

Es mejor usar ==en su lugar en este caso.

Más información está aquí: http://docs.python.org/2/c-api/int.html

CS Gamer
fuente
2

X apunta a una matriz, Y apunta a una matriz diferente. Esas matrices son idénticas, pero el isoperador observará esos punteros, que no son idénticos.

Neko
fuente
5
Python no tiene punteros. Necesita ajustar su terminología.
David Heffernan
3
Lo hace internamente, al igual que Java y tantos otros lenguajes. De hecho, la isfuncionalidad del operador lo demuestra.
Neko
5
Los detalles de implementación no son lo que importa. La documentación utiliza la terminología "identidad de objeto". Por lo que debería. "Los operadores son y no son una prueba de identidad de objeto: x es y es verdadero si y solo si xey son el mismo objeto. X no es y produce el valor de verdad inverso".
David Heffernan
1
@Neko: CPython usa punteros internamente. Pero obviamente Jython (implementado en Java) y PyPy (implementado en un subconjunto de Python) no usan punteros. En PyPy, algunos objetos ni siquiera tendrán un a idmenos que lo solicites.
abarnert
1

Compara la identidad del objeto, es decir, si las variables se refieren al mismo objeto en la memoria. Es como ==en Java o C (al comparar punteros).

mipadi
fuente
1

Un ejemplo sencillo con frutas

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist is newfruitlist )
print ( fruitlist is verynewfruitlist )
print ( newfruitlist is verynewfruitlist )

Salida:

True
False
False

Si intentas

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist == newfruitlist )
print ( fruitlist == verynewfruitlist )
print ( newfruitlist == verynewfruitlist )

La salida es diferente:

True
True
True

Eso es porque el operador == compara solo el contenido de la variable. Para comparar las identidades de 2 variables use el operador is

Para imprimir el número de identificación:

print ( id( variable ) )
ForsakenOne
fuente
-3

El isoperador no es más que una versión en inglés de ==. Debido a que los ID de las dos listas son diferentes, la respuesta es falsa. Puedes probar:

a=[1,2,3]
b=a
print(b is a )#True

* Porque las ID de ambas listas serían las mismas

Aadit
fuente
isno es 'una versión en inglés de =='
David Buck