2010-05-18 7 views
56

consideran este código:¿Por qué la palabra clave "es" tiene un comportamiento diferente cuando hay un punto en la cadena?

>>> x = "google" 
>>> x is "google" 
True 
>>> x = "google.com" 
>>> x is "google.com" 
False 
>>> 

¿Por qué es así?

Para asegurarse de que todo lo anterior es correcto, acabo de probar en Python 2.5.4, 2.6.5, 2.7b2, Python 3.1 en Windows y Python 2.7b1 en Linux.

Parece que hay consistencia en todos ellos, por lo que es por diseño. ¿Me estoy perdiendo de algo?

Lo acabo de enterar de que parte de mi script de filtrado de dominio personal falló con eso.

+10

Nice catch. Eso es extraño. –

+2

Ambos son 'False' en Python 2.5.2. –

+3

En ActivePython 2.5.4.4, se ven los mismos resultados que OP. Casi seguro tiene algo que ver con el interrogatorio de cuerdas, ¿verdad? –

Respuesta

89

is verifica objeto de identidad, y cualquier aplicación de Python, cuando se reúna literal de tipos inmutables, es perfectamente libre de ya sea crea un nuevo objeto de ese tipo inmutable, o buscan a través de objetos existentes de ese tipo a ver si algunos de ellos podrían reutilizarse (agregando una nueva referencia al mismo objeto subyacente). Esta es una elección pragmática de optimización y no sujeta a restricciones semánticas, por lo que su código nunca debe depender de qué ruta puede tomar una implementación de entrega (¡o podría romperse con una versión de reparación/optimización de Python!).

Considérese, por ejemplo:

>>> import dis 
>>> def f(): 
... x = 'google.com' 
... return x is 'google.com' 
... 
>>> dis.dis(f) 
    2   0 LOAD_CONST    1 ('google.com') 
       3 STORE_FAST    0 (x) 

    3   6 LOAD_FAST    0 (x) 
       9 LOAD_CONST    1 ('google.com') 
      12 COMPARE_OP    8 (is) 
      15 RETURN_VALUE  

por lo que en esta implementación particular, dentro de una función, su observación no se aplica y se hace solamente un objeto para el literal (cualquier literal), y, en efecto:

>>> f() 
True 

pragmático eso es porque dentro de una función que hace un paso a través de la tabla local de constantes (para ahorrar espacio en la memoria no hacer múltiples objetos inmutables constantes donde uno es suficiente) es bastante es barato y rápido, y puede ofrecer un buen rendimiento ya que la función puede ser llamada repetidamente después.

embargo, la misma aplicación, desde el modo interactivo (Editar: Al principio pensé que esto también sucedería en el nivel superior de un módulo, pero un comentario de @Thomas me puso la derecha, véase más adelante):

>>> x = 'google.com' 
>>> y = 'google.com' 
>>> id(x), id(y) 
(4213000, 4290864) 

NO se molesta en intentar guardar la memoria de esa manera - los id s son diferentes, es decir, objetos distintos. Hay costos potencialmente más altos y rendimientos más bajos, por lo que la heurística del optimizador de esta implementación le indica que no se moleste en buscar y solo siga adelante. por ejemplo, en el nivel superior del módulo, y por observación @Thomas', teniendo en cuenta::

Editar

$ cat aaa.py 
x = 'google.com' 
y = 'google.com' 
print id(x), id(y) 

más vemos la memoria de optimización basado en tablas-de-constantes en esta implementación:

>>> import aaa 
4291104 4291104 

(final de Edición por observación de @Thomas ').

Por último, de nuevo en la misma aplicación:

>>> x = 'google' 
>>> y = 'google' 
>>> id(x), id(y) 
(2484672, 2484672) 

las heurísticas son diferentes aquí porque la cadena literal "parece que podría ser un identificador" - por lo que podría ser utilizado en la operación que requiere internar .. .por lo que el optimizador lo interna de todos modos (y una vez internado, buscarlo se vuelve muy rápido, por supuesto). Y, en efecto, sorpresa, sorpresa ...:

>>> z = intern(x) 
>>> id(z) 
2484672 

... xtiene sido intern ed primera vez (como se ve, el valor de retorno de internes el mismo objeto comox y y , ya que tiene el mismo id()). Por supuesto, tampoco debe confiar en esto: el optimizador no tiene tiene para internar todo automáticamente, es solo una heurística de optimización; si necesita intern cadena ed, intern explícitamente, solo para estar seguros. Cuando haces cadenas intern ... explícitamente:

>>> x = intern('google.com') 
>>> y = intern('google.com') 
>>> id(x), id(y) 
(4213000, 4213000) 

... entonces hace garantizar exactamente el mismo objeto (es decir, los mismos resultados id()) cada uno y cada vez - para que pueda aplicar micro -optimizaciones como comprobar con is en lugar de == (casi nunca he encontrado la ganancia de rendimiento minúsculo que merezca la pena ;-).

Editar: sólo para aclarar, he aquí el tipo de diferencias de rendimiento que estoy hablando, en un lento Macbook Air ...:

$ python -mtimeit -s"a='google';b='google'" 'a==b' 
10000000 loops, best of 3: 0.132 usec per loop 
$ python -mtimeit -s"a='google';b='google'" 'a is b' 
10000000 loops, best of 3: 0.107 usec per loop 
$ python -mtimeit -s"a='goo.gle';b='goo.gle'" 'a==b' 
10000000 loops, best of 3: 0.132 usec per loop 
$ python -mtimeit -s"a='google';b='google'" 'a is b' 
10000000 loops, best of 3: 0.106 usec per loop 
$ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a is b' 
10000000 loops, best of 3: 0.0966 usec per loop 
$ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a == b' 
10000000 loops, best of 3: 0.126 usec per loop 

... unas pocas decenas de nanosegundos, ya sea manera, a lo sumo. Por lo tanto, vale la pena incluso pensando sólo en el extremo "optimizar el [eliminó impropio] fuera de este [embrollo suprimido] cuello de botella de rendimiento" situaciones! -)

+8

Parece que estoy haciendo mal uso de ** es ** operador – YOU

+1

@ S.Mark, posiblemente, pero no necesariamente, vea mi edición sobre prácticas. Normalmente debería usar 'is' solo en tablas mutables como listas, y singletons como' None', pero si ha asegurado la internación (como una optimización verdaderamente microscópica) también podría usarla allí (el internado también hace controles '==' un poquito más rápido, así que puede que no necesites insertar 'is' incluso si ** haces ** prácticas religiosas todas las cadenas relevantes! -). –

+2

Alex, dices "en el nivel superior del módulo (o el indicador interactivo)", pero creo que lo que describes (y lo que vio el OP) * solo * sucede en el indicador interactivo; el nivel superior del módulo aún está compilado en un solo objeto de código, y todas las referencias a la misma constante a lo largo de ese objeto de código utilizan la misma referencia. –

15

"es" es una prueba de identidad. Python tiene cierto comportamiento de almacenamiento en caché para enteros pequeños y (aparentemente) cadenas. "es" se utiliza mejor para las pruebas de singleton (por ejemplo, None).

>>> x = "google" 
>>> x is "google" 
True 
>>> id(x) 
32553984L 
>>> id("google") 
32553984L 
>>> x = "google.com" 
>>> x is "google.com" 
False 
>>> id(x) 
32649320L 
>>> id("google.com") 
37787888L 
Cuestiones relacionadas