2010-11-04 14 views
6

¿Podría alguien explicarme este extraño resultado en Python 2.6.6?Extraño resultado en Python

>>> a = "xx" 
>>> b = "xx" 
>>> a.__hash__() == b.__hash__() 
True 
>>> a is b 
True # ok.. was just to be sure 

>>> a = "x" * 2 
>>> b = "x" * 2 
>>> a.__hash__() == b.__hash__() 
True 
>>> a is b 
True # yeah.. looks ok so far ! 

>>> n = 2 
>>> a = "x" * n 
>>> b = "x" * n 
>>> a.__hash__() == b.__hash__() 
True # still okay.. 
>>> a is b 
False # hey! What the F... ? 
+5

¿Dónde diablos las personas aprenden sobre 'is', pero no sobre cómo es diferente de' == '? – delnan

+0

posible duplicado de [Python '==' vs 'es' comparar cadenas, 'es' falla algunas veces, ¿por qué?] (Http://stackoverflow.com/questions/1504717/python-vs-is-comparing-strings-is -fails-sometimes-why) – SilentGhost

+0

@SilentGhost: No exactamente, ya que esto se refiere al tema de cuándo los compiladores podrían internar cadenas inesperadamente. –

Respuesta

12

Para comprender esto, debe comprender algunas cosas diferentes.

  • a is b devuelve verdadero si a y b son el mismo objeto, no sólo si tienen el mismo valor . Las cadenas pueden tener el mismo valor pero ser una instancia diferente de ese valor.
  • Cuando dice a = "x", lo que en realidad está haciendo es crear una constante de cadena "x" y luego asignarle un nombre, a. Las constantes de cadena son cadenas escritas literalmente en el código y no calculadas mediante programación. Las constantes de cadena siempre son internados, lo que significa que están almacenados en una tabla para su reutilización: si dices a = "a"; b = "a", en realidad es lo mismo que decir a = "a"; b = a, ya que usarán la misma cadena "a". Es por eso que el primer a is b es verdadero.
  • Cuando dice a = "x" * 2, el compilador de Python en realidad está optimizando esto. Calcula la cadena en tiempo de compilación: genera el código como si hubiera escrito a = "xx". Por lo tanto, se intercala la cadena resultante "xx'. Es por eso que el segundo a is b es verdadero.
  • Cuando dice a = "x" * n, el compilador de Python no sabe qué n es en tiempo de compilación. Por lo tanto, está obligado a emitir realmente la cadena "x" y luego realizar la multiplicación de cadenas en tiempo de ejecución.Dado que eso se realiza en tiempo de ejecución, mientras que "x" está internado, la cadena resultante "xx" es y no. Como resultado, cada una de estas cadenas son instancias diferentes de "xx", por lo que el a is b final es False.

Se puede ver la diferencia usted mismo:

def a1(): 
    a = "x" 
def a2(): 
    a = "x" * 2 
def a3(): 
    n = 2 
    a = "x" * n 


import dis 
print "a1:" 
dis.dis(a1) 

print "a2:" 
dis.dis(a2) 

print "a3:" 
dis.dis(a3) 

En CPython 2.6.4, esta salidas:

a1: 
    4   0 LOAD_CONST    1 ('x') 
       3 STORE_FAST    0 (a) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE 
a2: 
    6   0 LOAD_CONST    3 ('xx') 
       3 STORE_FAST    0 (a) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE 
a3: 
    8   0 LOAD_CONST    1 (2) 
       3 STORE_FAST    0 (n) 

    9   6 LOAD_CONST    2 ('x') 
       9 LOAD_FAST    0 (n) 
      12 BINARY_MULTIPLY 
      13 STORE_FAST    1 (a) 
      16 LOAD_CONST    0 (None) 
      19 RETURN_VALUE 

Por último, tenga en cuenta que se puede decir a = intern(a); b = intern(b) para crear versiones internados si el cadenas, lo que garantizará que a is b es verdadero. Si todo lo que desea es verificar la igualdad de cadenas, sin embargo, solo use a == b.

+0

Upvoted for absolute comprehensiveness. – kindall

+0

Sobre pasante, encontré las cuerdas inferiores o iguales a 20 caracteres son automáticamente internados por Python. Mi verdadera pregunta era, ¿por qué "x" * 2 (interno) era diferente de "x" * n .. (no internado, incluso si n <= 20) – pyrou

17

El operador is le indica si dos variables apuntan al mismo objeto en la memoria. Rara vez es útil y a menudo se confunde con el operador ==, que le indica si dos objetos "tienen el mismo aspecto".

Es particularmente confuso cuando se utiliza con cosas como literales de cadenas cortas, porque el compilador de Python las interna para su eficacia. En otras palabras, cuando escribe "xx" el compilador (emite bytecode that) crea un objeto de cadena en la memoria y hace que todos los literales "xx" apunten a él. Esto explica por qué tus primeras dos comparaciones son verdaderas. Observe que puede obtener la Identificación de las cuerdas llamando id en ellos, que (al menos en CPython es probablemente) su dirección en la memoria:

>>> a = "xx" 
>>> b = "xx" 
>>> id(a) 
38646080 
>>> id(b) 
38646080 
>>> a is b 
True 
>>> a = "x"*10000 
>>> b = "x"*10000 
>>> id(a) 
38938560 
>>> id(b) 
38993504 
>>> a is b 
False 

El tercero es porque el compilador no ha internado las cuerdas a y b, por la razón que sea (probablemente porque no es lo suficientemente inteligente como para notar que la variable n se define una vez y luego nunca se modifica).

En realidad, puede forzar a Python a aplicar cadenas internas, bueno, asking it to. Esto le dará una cantidad insignificante de aumento de rendimiento y podría ayudar. Probablemente sea inútil.

Moral: no use is con cadenas literales. O int literales. O en cualquier lugar, no lo dices en serio.

+0

Merece la pena agregar una explicación de por qué funcionan las dos primeras llamadas 'a es b'. –

+0

Y si desea seguir inspeccionando, pruebe los resultados de 'id (a)' y 'id (b)' para los tres casos. – user470379

+0

* por cualquier razón *. Bueno, la razón es bastante obvia, ¿no? porque el compilador no produce código de bytes para esas cadenas n-long. – SilentGhost

Cuestiones relacionadas