¿Por qué comparar cadenas usando '==' o 'es' a veces produce un resultado diferente?

1147

Tengo un programa Python donde dos variables se establecen en el valor 'public'. En una expresión condicional tengo la comparación var1 is var2que falla, pero si la cambio a var1 == var2ella, regresa True.

Ahora, si abro mi intérprete de Python y hago la misma comparación "es", tiene éxito.

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

¿Que me estoy perdiendo aqui?

jottos
fuente
8
ver: stackoverflow.com/questions/1392433/…
Nick Dandoulakis 01 de
3
Este problema también se produce cuando se lee una entrada de la consola a través de, por ejemplo: input = raw_input("Decide (y/n): "). En este caso, una entrada de "y" y if input == 'y':devolverá "True" mientras if input is 'y':que devolverá False.
Semjon Mössinger
44
Este blog proporciona una explicación mucho más completa que cualquier respuesta guilload.com/python-string-interning
Chris_Rands
1
Como @ chris-rico menciona, aquí tengo una gran explicación stackoverflow.com/q/15541404/1695680
ThorSummoner

Respuestas:

1533

ises prueba de identidad, ==es prueba de igualdad. lo que sucede en su código se emularía en el intérprete así:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

así que no es de extrañar que no sean lo mismo, ¿verdad?

En otras palabras: ises elid(a) == id(b)

SilentGhost
fuente
17
ahh igual que eq? vs igual? en el esquema, lo tengo.
jottos el
47
O ==vs .equals()en Java. La mejor parte es que Python ==no es análogo a Java ==.
MatrixFrog
11
@ Крайст: solo hay un Nonevalor único . Entonces siempre tiene la misma identificación.
SilentGhost
18
Esto no aborda el ejemplo "es -> Verdadero" del OP.
user2864740
66
@AlexanderSupertramp, debido al internamiento de cadenas .
Chris Rico
570

Otras respuestas aquí son correctas: isse usa para la comparación de identidad , mientras que ==se usa para la comparación de igualdad . Como lo que le importa es la igualdad (las dos cadenas deben contener los mismos caracteres), en este caso, el isoperador simplemente está equivocado y debería usarlo en su ==lugar.

La razón por la que isfunciona de manera interactiva es que (la mayoría) los literales de cadena están internados por defecto. De Wikipedia:

Las cadenas internas aceleran las comparaciones de cadenas, que a veces son un cuello de botella de rendimiento en aplicaciones (como compiladores y tiempos de ejecución de lenguaje de programación dinámico) que dependen en gran medida de tablas hash con claves de cadena. Sin pasantías, comprobar que dos cadenas diferentes son iguales implica examinar cada carácter de ambas cadenas. Esto es lento por varias razones: es inherentemente O (n) en la longitud de las cadenas; normalmente requiere lecturas de varias regiones de memoria, lo que lleva tiempo; y las lecturas llenan el caché del procesador, lo que significa que hay menos caché disponible para otras necesidades. Con cadenas internas, una simple prueba de identidad de objeto es suficiente después de la operación interna original; esto normalmente se implementa como una prueba de igualdad de puntero,

Entonces, cuando tiene dos literales de cadena (palabras que están literalmente escritas en el código fuente de su programa, rodeadas de comillas) en su programa que tienen el mismo valor, el compilador de Python internará automáticamente las cadenas, haciéndolas ambas almacenadas en el mismo ubicación de memoria (Tenga en cuenta que esto no siempre sucede, y las reglas para cuando esto sucede son bastante complicadas, ¡así que no confíe en este comportamiento en el código de producción!)

Dado que en su sesión interactiva ambas cadenas se almacenan en la misma ubicación de memoria, tienen la misma identidad , por lo que el isoperador funciona como se esperaba. Pero si construye una cadena por algún otro método (incluso si esa cadena contiene exactamente los mismos caracteres), entonces la cadena puede ser igual , pero no es la misma cadena , es decir, tiene una identidad diferente , porque es almacenado en un lugar diferente en la memoria.

Daniel Pryden
fuente
66
¿Dónde puede alguien leer más sobre las reglas complicadas para cuando se internan cadenas?
Noctis Skytower
89
+1 para una explicación detallada. No estoy seguro de cómo la otra respuesta recibió tantos votos positivos sin explicar lo que REALMENTE sucedió.
That1Guy
44
esto es exactamente lo que pensé cuando leí la pregunta. La respuesta aceptada es breve pero contiene el hecho, pero esta respuesta explica las cosas mucho mejor. ¡Agradable!
Sнаđошƒаӽ
3
@NoctisSkytower buscó en Google lo mismo y encontró este guilload.com/python-string-interning
xtreak
55
@ naught101: No, la regla es elegir entre ==y issegún el tipo de verificación que desee. Si le preocupa que las cadenas sean iguales (es decir, que tengan el mismo contenido), siempre debe usarlas ==. Si le importa si dos nombres de Python se refieren a la misma instancia de objeto, debe usarlos is. Es posible que necesite issi está escribiendo código que maneja muchos valores diferentes sin preocuparse por su contenido, o si sabe que solo hay uno de algo y desea ignorar otros objetos que fingen ser esa cosa. Si no está seguro, elija siempre ==.
Daniel Pryden
108

La ispalabra clave es una prueba de identidad de objeto, mientras que ==es una comparación de valores.

Si lo usa is, el resultado será verdadero si y solo si el objeto es el mismo objeto. Sin embargo, ==será cierto siempre que los valores del objeto sean los mismos.

Thomas Owens
fuente
57

Una última cosa a tener en cuenta es que puede usar la sys.internfunción para asegurarse de obtener una referencia a la misma cadena:

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

Como se señaló anteriormente, no debe usarse ispara determinar la igualdad de cadenas. Pero esto puede ser útil para saber si tiene algún tipo de requisito extraño para usar is.

Tenga en cuenta que la internfunción solía estar integrada en Python 2 pero se movió al sysmódulo en Python 3.

Jason Baker
fuente
43

ises prueba de identidad, ==es prueba de igualdad. Lo que esto significa es que ises una forma de verificar si dos cosas son las mismas , o simplemente equivalentes.

Digamos que tienes un personobjeto simple . Si se llama 'Jack' y tiene '23' años, es equivalente a otro Jack de 23 años, pero no es la misma persona.

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def __eq__(self, other):
       return self.name == other.name and self.age == other.age

jack1 = Person('Jack', 23)
jack2 = Person('Jack', 23)

jack1 == jack2 #True
jack1 is jack2 #False

Son de la misma edad, pero no son la misma instancia de persona. Una cadena puede ser equivalente a otra, pero no es el mismo objeto.

TankorSmash
fuente
Si cambia el conjunto jack1.age = 99, eso no cambiará jack2.age. Eso es porque son dos instancias diferentes, entonces jack1 is not jack2. Sin embargo, pueden igualarse entre sí jack1 == jack2si su nombre y su edad son los mismos. Se vuelve más complicado para las cadenas, porque las cadenas son inmutables en Python, y Python a menudo reutiliza la misma instancia. Me gusta esta explicación porque usa los casos simples (un objeto normal) en lugar de los casos especiales (cadenas).
Flimm
37

Esta es una nota al margen, pero en Python idiomático, a menudo verá cosas como:

if x is None: 
    # some clauses

Esto es seguro, porque se garantiza que habrá una instancia del Objeto nulo (es decir, Ninguno) .

Gregg Lind
fuente
1
¿Es lo mismo cierto para Verdadero y Falso? ¿Solo una instancia será igual?
HandyManDan
1
@HandyManDan Sí, son singletons tanto en python 2 como en 3.
kamillitw
@kamillitw pero en Python 2 puedes reasignar False y True.
Martijn Pieters
28

Si no está seguro de lo que está haciendo, use '=='. Si tiene un poco más de conocimiento al respecto, puede usar 'es' para objetos conocidos como 'Ninguno'.

De lo contrario, terminarás preguntándote por qué las cosas no funcionan y por qué sucede esto:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

Ni siquiera estoy seguro de que algunas cosas se mantengan igual entre diferentes versiones / implementaciones de Python.

Mattias Nilsson
fuente
1
Un ejemplo interesante que muestra cómo reasignar ints hace que se active esta condición. ¿Por qué falló esto? ¿Se debe a una pasantía o algo más?
Paul
Parece que la razón por la cual se devuelve falso puede deberse a la implementación del intérprete: stackoverflow.com/questions/132988/…
Paul
@ArchitJain Sí, esos enlaces lo explican bastante bien. Cuando los lea, sabrá en qué números puede usar 'is'. Solo desearía que explicaran por qué todavía no es una buena idea hacer eso :) Sabiendo que esto no hace que sea una buena idea asumir que todos los demás también lo hacen (o que el rango de números internalizado nunca cambiará)
Mattias Nilsson
20

Desde mi experiencia limitada con python, isse utiliza para comparar dos objetos para ver si son el mismo objeto en oposición a dos objetos diferentes con el mismo valor. ==se usa para determinar si los valores son idénticos.

Aquí hay un buen ejemplo:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1es una cadena unicode y s2es una cadena normal. No son del mismo tipo, pero tienen el mismo valor.

Jack M.
fuente
17

Creo que tiene que ver con el hecho de que, cuando la comparación 'es' se evalúa como falsa, se utilizan dos objetos distintos. Si se evalúa como verdadero, eso significa que internamente está usando el mismo objeto exacto y no está creando uno nuevo, posiblemente porque los creó en una fracción de 2 segundos más o menos y porque no hay una gran brecha de tiempo entre está optimizado y usa el mismo objeto

Es por eso que debería usar el operador de igualdad ==, no is, para comparar el valor de un objeto de cadena.

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

En este ejemplo, hice s2, que era un objeto de cadena diferente previamente igual a 'uno' pero no es el mismo objeto que s, porque el intérprete no usó el mismo objeto ya que inicialmente no lo asigné a 'uno', si lo hubiera hecho les habría hecho el mismo objeto.

meder omuraliev
fuente
3
Sin .replace()embargo, usarlo como ejemplo en este contexto probablemente no sea el mejor, porque su semántica puede ser confusa. s2 = s2.replace()será siempre crear un nuevo objeto de cadena, asignar el nuevo objeto de cadena a s2, y luego disponer del objeto cadena que s2utiliza para el punto a. Entonces, incluso si lo hiciera s = s.replace('one', 'one'), aún obtendría un nuevo objeto de cadena.
Daniel Pryden
13

Creo que esto se conoce como cadenas "internas". Python hace esto, también Java, y también C y C ++ cuando compila en modos optimizados.

Si usa dos cadenas idénticas, en lugar de desperdiciar memoria creando dos objetos de cadena, todas las cadenas internas con el mismo contenido apuntan a la misma memoria.

Esto da como resultado que el operador "is" de Python devuelva True porque dos cadenas con el mismo contenido apuntan al mismo objeto de cadena. Esto también sucederá en Java y en C.

Sin embargo, esto solo es útil para ahorrar memoria. No puede confiar en él para probar la igualdad de cadena, porque los diversos intérpretes y compiladores y motores JIT no siempre pueden hacerlo.

Zan Lynx
fuente
12

Estoy respondiendo la pregunta a pesar de que la pregunta es demasiado antigua porque ninguna de las respuestas anteriores cita la referencia del idioma

En realidad, el operador verifica la identidad y == el operador verifica la igualdad,

De la referencia del lenguaje:

Los tipos afectan a casi todos los aspectos del comportamiento del objeto. Incluso la importancia de la identidad del objeto se ve afectada en cierto sentido: para los tipos inmutables, las operaciones que calculan nuevos valores en realidad pueden devolver una referencia a cualquier objeto existente con el mismo tipo y valor, mientras que para los objetos mutables esto no está permitido . Por ejemplo, después de a = 1; b = 1, ayb pueden o no referirse al mismo objeto con el valor uno, dependiendo de la implementación, pero después de c = []; d = [], cyd están garantizados para referirse a dos listas vacías diferentes, únicas y recién creadas. (Tenga en cuenta que c = d = [] asigna el mismo objeto a c y d.)

entonces, de la declaración anterior, podemos inferir que las cadenas que son de un tipo inmutable pueden fallar cuando se verifican con "es" y pueden ser exitosas cuando se marcan con "es"

Lo mismo aplica para int, tuple que también son tipos inmutables

RAM
fuente
8

La ==equivalencia del valor de prueba del operador. El isoperador prueba la identidad del objeto, Python prueba si los dos son realmente el mismo objeto (es decir, viven en la misma dirección en la memoria).

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

En este ejemplo, Python solo creó un objeto de cadena, y ambos, ay se brefiere a él. La razón es que Python almacena en caché internamente y reutiliza algunas cadenas como una optimización, realmente solo hay una cadena 'banana' en la memoria, compartida por a y b; Para desencadenar el comportamiento normal, debe usar cadenas más largas:

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

Cuando crea dos listas, obtiene dos objetos:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

En este caso, diríamos que las dos listas son equivalentes, porque tienen los mismos elementos, pero no idénticos, porque no son el mismo objeto. Si dos objetos son idénticos, también son equivalentes, pero si son equivalentes, no son necesariamente idénticos.

Si se arefiere a un objeto y usted lo asigna b = a, ambas variables se refieren al mismo objeto:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
X. Wang
fuente
7

iscomparará la ubicación de la memoria. Se utiliza para la comparación a nivel de objeto.

==comparará las variables en el programa. Se utiliza para verificar en un nivel de valor.

is comprueba la equivalencia de nivel de dirección

== comprueba la equivalencia de nivel de valor

johnashu
fuente
3

ises una prueba de identidad, ==es una prueba de igualdad (consulte la documentación de Python ).

En la mayoría de los casos, si a is b, entonces a == b. Pero hay excepciones, por ejemplo:

>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

Por lo tanto, solo puede usar ispara pruebas de identidad, nunca pruebas de igualdad.

Ryan
fuente