2010-07-02 10 views
5

Como sabe cualquier programador de Python, debe usar == en lugar de is para comparar dos cadenas para la igualdad. Sin embargo, ¿hay realmente algún caso donde (s is "") y (s == "") darán resultados diferentes en Python 2.6.2?¿Puede (s es "") y (s == "") dar resultados diferentes en Python 2.6.2?

Hace poco encontré el código que usa (s is "") en la revisión del código, y aunque señalé que esto era incorrecto, quería dar un ejemplo de cómo podría fallar. Pero por más que lo intente, no puedo construir dos cadenas vacías con identidades diferentes. Parece que la implementación de Python debe tener un caso especial en la cadena vacía en muchas operaciones comunes. Por ejemplo:

>>> a = "" 
>>> b = "abc"[ 2:2 ] 
>>> c = ''.join([]) 
>>> d = re.match('()', 'abc').group(1) 
>>> e = a + b + c + d 
>>> a is b is c is d is e 
True 

Sin embargo, this question sugiere que son casos en que (s is "") y (s == "") puede ser diferente. ¿Alguien puede darme un ejemplo?

Respuesta

11

Como han dicho todos los demás, no confíe en un comportamiento indefinido. Sin embargo, ya que preguntas para un contraejemplo específico para Python 2.6, aquí está:

>>> s = u"\xff".encode('ascii', 'ignore') 
>>> s 
'' 
>>> id(s) 
10667744 
>>> id("") 
10666064 
>>> s == "" 
True 
>>> s is "" 
False 
>>> type(s) is type("") 
True 

La única vez que Python 2.6 puede terminar con una cadena vacía que no es la cadena vacía normal, es decir cuando se hace una cadena operación y no está seguro de antemano cuánto tiempo será la cadena. Entonces, cuando codifica una cadena, el manejador de errores puede terminar eliminando caracteres y reparando el tamaño del búfer una vez que se ha completado. Por supuesto, eso es un descuido y podría cambiar fácilmente en Python 2.7.

12

Python is prueba la identidad de los objetos y no la igualdad. He aquí un ejemplo en el que el uso de is y == da un resultado diferente:

>>> s=u"" 
>>> print s is "" 
False 
>>> print s=="" 
True 
+1

Sin embargo, esto prueba dos tipos diferentes de objetos de cadena, 'str' y' unicode'. La pregunta aún permanece cuando s es del mismo tipo, o si usas Python 3 en el que ya no hay un objeto explícito 'unicode'. – poke

+1

@poke, la pregunta explícitamente se centra en Python 2.6.2 y no en Python 3. – zoli2k

+0

Es cierto, pero es una distinción importante que hacer y esta probablemente no sea la forma más amigable de hacerlo. ¿Qué prueba realmente significaba ese código? ¿Que 'la cosa pasada adentro era equivalente a una cuerda vacía' o que 'la cosa pasada adentro era de tipo str y vacía'? Imo si fuera el último, entonces una prueba explícita de tipo sería más amigable para los futuros mantenedores y usuarios. – pycruft

3

Parece que funciona para nada, que realidad es una cadena, sino algo que simplemente parece una cadena (por ejemplo, un Unicode o subclase de str o algo similar) fallará.

>>> class mysub(str): 
    def __init__(self, *args, **kwargs): 
     super(mysub, self).__init__(*args, **kwargs) 

>>> 
>>> q = mysub("") 
>>> q is "" 
False 
>>> q == "" 
True 

edición:

A los efectos de la revisión de código & retroalimentación que permite suponer que no era mala práctica ya que implementa una prueba inesperada (incluso si ignoramos la incertidumbre de si siempre se comportará lo mismo cuando los tipos coinciden).

if x is "" 

implica que x es el valor correcto de y tipo, pero sin una prueba explícita del tipo que advertir a los mantenedores o usuarios de la API futuro, etc.

if x == "" 

Implica que x es sólo del valor correcto

+0

Y, por supuesto, serán diferentes para un objeto de una clase no relacionada con str, pero que implementa \ _ \ _ eq \ _ \ _ de tal manera que se compare igual a una cadena. – jchl

7

No debería importarle. A diferencia de None que se define como singleton, no hay ninguna regla que diga que solo hay un objeto de cadena vacío. Por lo tanto, el resultado de s is "" depende de la implementación y usar is es NO-NO, ya sea que pueda encontrar un ejemplo o no.

+0

Entiendo eso. Pero a los efectos de dar retroalimentación de revisión de código a un programador junior, es mejor decir "no hagas eso porque no funciona en esta situación" que decir "no hagas eso porque está mal". Por supuesto, si "la causa es incorrecta" es la única razón, entonces esa es la razón por la que tendré que dar ... – jchl

+1

Bueno, debería darle una visión general a su programador junior. Es mejor saber que "es" está probando para algo más que '==' y, por lo tanto, debe usarse para su propósito correcto; el hecho incidental de que pueda salirse con la suya en algunas circunstancias es bastante irrelevante, tratando de aprender el las circunstancias donde un antipattern * does * work es un esfuerzo desperdiciado y una receta segura para futuros errores. –

+0

Debería decir: "No hagas eso porque está mal. Está mal porque' is' no debería usarse para verificar si dos valores son iguales, solo para verificar si son exactamente la misma instancia ". – Blixt

0

Probablemente no, CPython parece optimizar las instancias espurias de "" de distancia en todos los casos. Pero como dicen los demás, no confíe en eso.

1

No puede encontrar un ejemplo porque algunas cosas son únicas y no se pueden silenciar, por lo que Python las mantiene exactamente una vez y por lo tanto is funciona. Estos incluyen (), '',u'', True, False, None CPython incluso mantiene algunos números de uso frecuente, es decir, 0, 0.0, 1, 1.0,

+2

Experimentalmente, parece que CPython (versión 2.6.2) realmente interna todos los enteros de -5 a 256. – jchl

+0

'0.0' y' 1.0' no deberían estar en la lista anterior: los flotantes no son internados por CPython actual. –

0

El comportamiento indefinido es un problema poco claro. Hay cosas que la especificación de Python define y las implementaciones adheridas deben cumplir, y hay cosas que quedan por elegir.Puede convencerse, al observar el código fuente de Python, que este comportamiento nunca puede ocurrir para los objetos de cadena reales (a diferencia de los ejemplos de unicode y no unicode y otros ejemplos cercanos pero irrelevantes). Feliz, dejarás esa prueba en un código.

Pero la implementación de Python no garantiza que siempre funcione. Alguna implementación futura puede hacer que cambie y tendrá una incompatibilidad dolorosa.

Así que la regla general con esto es simple: no lo hagas. Use operadores solo para su uso previsto y bien documentado. No confíe en artefactos de implementación que muy bien pueden cambiar en el futuro.

Cuestiones relacionadas