Establecer operador “in”: utiliza la igualdad o la identidad?

class A(object): def __cmp__(self): print '__cmp__' return object.__cmp__(self) def __eq__(self, rhs): print '__eq__' return True a1 = A() a2 = A() print a1 in set([a1]) print a1 in set([a2]) 

¿Por qué la primera línea imprime verdadero, pero la segunda imprime falso? ¿Y tampoco entra el operador eq ?

Estoy usando Python 2.6

Necesitas definir __hash__ también. Por ejemplo

 class A(object): def __hash__(self): print '__hash__' return 42 def __cmp__(self, other): print '__cmp__' return object.__cmp__(self, other) def __eq__(self, rhs): print '__eq__' return True a1 = A() a2 = A() print a1 in set([a1]) print a1 in set([a2]) 

Funcionará como se espera.

Como regla general, cada vez que implementes __cmp__ deberías implementar un __hash__ tal que para todas las x e y tal que x == y , x.__hash__() == y.__hash__() .

Establecer __contiene__ hace cheques en el siguiente orden:

  'Match' if hash(a) == hash(b) and (a is b or a==b) else 'No Match' 

El código fuente de C relevante está en Objetos / setobject.c :: set_lookkey () y en Objetos / object.c :: PyObject_RichCompareBool ().

Los conjuntos y los diccionarios ganan velocidad al utilizar el hash como una aproximación rápida de la verificación de igualdad total. Si desea redefinir la igualdad, generalmente necesita redefinir el algoritmo hash para que sea consistente.

La función hash predeterminada usa la identidad del objeto, que es bastante inútil como una aproximación rápida de igualdad total, pero al menos le permite usar una instancia de clase arbitraria como clave de diccionario y recuperar el valor almacenado con él si pasa exactamente el mismo objeto que una llave. Pero significa que si redefine la igualdad y no redefine la función hash, sus objetos entrarán en un conjunto / diccionario sin quejarse de no ser hashable, pero aún así no funcionarán de la forma que espera.

Ver los documentos oficiales de python en __hash__ para más detalles.

Una respuesta tangencial, pero tu pregunta y mi prueba me dieron curiosidad. Si ignora el operador del conjunto que es la fuente de su problema __hash__ , resulta que su pregunta sigue siendo interesante.

Gracias a la ayuda que obtuve en esta pregunta SO , pude perseguir el operador in a través del código fuente hasta su raíz. Cerca de la parte inferior, encontré la función PyObject_RichCompareBool que de hecho prueba la identidad (ver el comentario sobre “Resultado rápido”) antes de probar la igualdad.

Entonces, a menos que malinterprete la forma en que funcionan las cosas, la respuesta técnica a su pregunta es primero la identidad y luego la igualdad, a través de la prueba de igualdad en sí. Solo para reiterar, esa no es la fuente del comportamiento que estaba viendo, sino solo la respuesta técnica a su pregunta.

Si malentendí la fuente, alguien, por favor, aclarame.

 int PyObject_RichCompareBool(PyObject *v, PyObject *w, int op) { PyObject *res; int ok; /* Quick result when objects are the same. Guarantees that identity implies equality. */ if (v == w) { if (op == Py_EQ) return 1; else if (op == Py_NE) return 0; } res = PyObject_RichCompare(v, w, op); if (res == NULL) return -1; if (PyBool_Check(res)) ok = (res == Py_True); else ok = PyObject_IsTrue(res); Py_DECREF(res); return ok; } 

Los conjuntos parecen usar códigos hash, luego identidad, antes de comparar la igualdad. El siguiente código:

 class A(object): def __eq__(self, rhs): print '__eq__' return True def __hash__(self): print '__hash__' return 1 a1 = A() a2 = A() print 'set1' set1 = set([a1]) print 'set2' set2 = set([a2]) print 'a1 in set1' print a1 in set1 print 'a1 in set2' print a1 in set2 

salidas:

 set1 __hash__ set2 __hash__ a1 in set1 __hash__ True a1 in set2 __hash__ __eq__ True 

Lo que sucede parece ser:

  1. El código hash se calcula cuando un elemento se inserta en un hash. (Para comparar con los elementos existentes.)
  2. Se calcula el código hash para el objeto que está verificando con el operador in .
  3. Los elementos del conjunto con el mismo código hash se inspeccionan comprobando primero si son el mismo objeto que el que está buscando o si son lógicamente iguales a él.