Confundido sobre el operador `is` con cuerdas

El operador is compara las direcciones de memoria de dos objetos y devuelve True si son iguales. ¿Por qué, entonces, no funciona de forma fiable con cadenas? Código # 1

 >>> a = "poi" >>> b = "poi" >>> a is b True 

Código # 2

 >>> ktr = "today is a fine day" >>> ptr = "today is a fine day" >>> ktr is ptr False 

He creado dos cadenas cuyo contenido es el mismo pero viven en direcciones de memoria diferentes. ¿Por qué la salida del operador no es consistente?

Creo que tiene que ver con el internado de cuerdas. En esencia, la idea es almacenar solo una copia única de cada cadena distinta, para boost el rendimiento en algunas operaciones.

Básicamente, la razón por la que a is b funciona es porque (como puede haber adivinado) hay una única cadena inmutable a la que Python hace referencia en ambos casos. Cuando una cadena es grande (y algunos otros factores que no entiendo, lo más probable), esto no se hace, por lo que su segundo ejemplo devuelve Falso.

EDIT: Y, de hecho, el comportamiento extraño parece ser un efecto secundario del entorno interactivo. Si toma su mismo código y lo coloca en una secuencia de comandos de Python, tanto a is b como ktr is ptr devuelve True.

 a="poi" b="poi" print a is b # Prints 'True' ktr = "today is a fine day" ptr = "today is a fine day" print ktr is ptr # Prints 'True' 

Esto tiene sentido, ya que sería fácil para Python analizar un archivo de origen y buscar literales de cadena duplicados dentro de él. Si crea las cadenas dinámicamente, entonces se comporta de manera diferente incluso en un script.

 a="p" + "oi" b="po" + "i" print a is b # Oddly enough, prints 'True' ktr = "today is" + " a fine day" ptr = "today is af" + "ine day" print ktr is ptr # Prints 'False' 

En cuanto a por qué a is b todavía resulta verdadero, quizás la cadena asignada sea lo suficientemente pequeña como para justificar una búsqueda rápida a través de la colección internada, mientras que la otra no lo es?

is prueba de identidad. Funcionará en algunas cadenas más pequeñas (debido a la memoria caché) pero no en otras cadenas más grandes . Dado que str no es un ptr. [gracias erykson]

Ver este codigo

 >>> import dis >>> def fun(): ... str = 'today is a fine day' ... ptr = 'today is a fine day' ... return (str is ptr) ... >>> dis.dis(fun) 2 0 LOAD_CONST 1 ('today is a fine day') 3 STORE_FAST 0 (str) 3 6 LOAD_CONST 1 ('today is a fine day') 9 STORE_FAST 1 (ptr) 4 12 LOAD_FAST 0 (str) 15 LOAD_FAST 1 (ptr) 18 COMPARE_OP 8 (is) 21 RETURN_VALUE >>> id(str) 26652288 >>> id(ptr) 27604736 #hence this comparison returns false: ptr is str 

Note que las ID de str y ptr son diferentes.

PERO:

 >>> x = "poi" >>> y = "poi" >>> id(x) 26650592 >>> id(y) 26650592 #hence this comparison returns true : x is y 

Las identificaciones de x e y son las mismas. Por is tanto, el operador trabaja en “ids” y no en “igualdades”

Vea el enlace a continuación para una discusión sobre cuándo y por qué Python asignará una ubicación de memoria diferente para cadenas idénticas (lea la pregunta también).

Cuando Python asigna nueva memoria para cadenas idénticas

También sys.intern en python3.x y intern en python2.x lo ayudarán a asignar las cadenas en la misma ubicación de memoria, independientemente del tamaño de la cadena.

is no es lo mismo que == .

Básicamente, is comprueba si los dos objetos son iguales, mientras que == compara los valores de esos objetos (las cadenas, como todo en Python, son objetos).

Por lo tanto, lo que debe usar is cuando realmente sabe qué objetos está mirando (es decir, si ha creado los objetos o está comparando con None como lo indican los comentarios de la pregunta), y quiere saber si dos variables hacen referencia a la Exactamente el mismo objeto en la memoria.

Sin embargo, en sus ejemplos, está viendo objetos str que Python está manejando detrás de la escena, así que sin profundizar en cómo funciona Python, realmente no sabe qué esperar. Tendrías el mismo problema con int s o float s. Otras respuestas hacen un buen trabajo al explicar las cosas “detrás de la escena” (internado de cadenas), pero la mayoría de las veces no debería preocuparse por eso en la progtwigción del día a día.

Tenga en cuenta que esta es una optimización específica de CPython. Si desea que su código sea portátil, debe evitarlo. Por ejemplo, en PyPy.

 >>>> a = "hi" >>>> b = "hi" >>>> a is b False 

También vale la pena señalar que algo similar ocurre con los enteros pequeños.

 >>> a = 12 >>> b = 12 >>> a is b True 

en el que nuevamente no debería confiar, porque otras implementaciones podrían no incluir esta optimización.