2012-02-13 14 views
8

Considere la siguiente:Python 2: diferente significado de la 'en' palabra clave para los conjuntos y listas

class SomeClass(object): 

    def __init__(self, someattribute="somevalue"): 
     self.someattribute = someattribute 

    def __eq__(self, other): 
     return self.someattribute == other.someattribute 

    def __ne__(self, other): 
     return not self.__eq__(other) 

list_of_objects = [SomeClass()] 
print(SomeClass() in list_of_objects) 

set_of_objects = set([SomeClass()]) 
print(SomeClass() in set_of_objects) 

que será evaluado:

True 
False 

¿Puede alguien explicar por qué el 'en' palabra clave tiene un significado diferente para conjuntos y listas? Hubiera esperado que ambas devolvieran True, especialmente cuando el tipo que se está probando tiene métodos de igualdad definidos.

+1

Ver http://stackoverflow.com/questions/7549709/unexpected-behavior-for-python-set-contains ... por cierto, en Python 3 al ejecutar este código se insinúa lo que está sucediendo: "TypeError: tipo insabrable: ' SomeClass '" – DSM

+0

Gracias a todos, todo está claro ahora. – mskel

+0

Por cierto, usted sabe que su 'someattribute' aquí es un atributo ** class ** y no un atributo ** instancia **, ¿verdad? * Has * oído hablar de '__init__', ¿verdad? –

Respuesta

15

El significado es el mismo, pero la implementación es diferente. Las listas simplemente examinan cada objeto, verificando la igualdad, por lo que funciona para su clase. Establece los primeros hash de los objetos, y si no implementan el hash correctamente, el conjunto parece no funcionar.

Su clase define __eq__, pero no define __hash__, por lo que no funcionará correctamente para conjuntos o como claves de diccionarios. La regla para __eq__ y __hash__ es que dos objetos que __eq__ como True también deben tener hashes iguales. Por defecto, los objetos se basan en su dirección de memoria. Por lo tanto, sus dos objetos que son iguales según su definición no proporcionan el mismo hash, por lo que infringen la regla sobre __eq__ y __hash__.

Si proporciona una implementación de __hash__, funcionará correctamente. Para su código de ejemplo, podría ser:

def __hash__(self): 
    return hash(self.someattribute) 
+4

Esta es una de las cosas que Python 3 maneja más claramente: se negará a hacer un conjunto de cualquier objeto que no tenga un '__hash __()'. Python 2 tiene un '__hash __()' predeterminado que refleja la identidad del objeto en lugar de la igualdad. – lvc

+0

En realidad, lo que sucedió es que las clases clásicas se comportaron de la misma manera (no definir un método '__hash__' le daría un valor predeterminado que generó un TypeError si definió' __cmp__' y/o '__eq__', pero luego un estilo nuevo las clases se introdujeron (en Python 2.2) y ese comportamiento no se copió correctamente. Se omitió esa supervisión por suficientes lanzamientos que cambiarlos podría romper demasiado código, por lo que su reparación se retrasó hasta Python 3. –

1

Definir __hash__() método que corresponde a su método __eq__(). Example.

3

En prácticamente cualquier implementación de hashtable, incluida Python, si anulas el método de igualdad, debes anular el método de hashing (en Python, esto es __hash__). El operador in para listas simplemente comprueba la igualdad con cada elemento de la lista, que el operador in establece primero hash el objeto que está buscando, busca un objeto en esa ranura de la tabla hash y luego verifica si hay igualdad en la ranura Por lo tanto, si reemplaza __eq__ sin anular __hash__, no se le puede garantizar que el operador in de los conjuntos verifique en la ranura correcta.

Cuestiones relacionadas