2010-10-04 10 views
5

En mi código hay numerosas comparaciones para la igualdad de varios contenedores (lista, dict, etc.). Las claves y valores de los contenedores son de tipos float, bool, int y str. El built-in == y! = Funcionó perfectamente bien.comparación personalizada para contenedores integrados

Acabo de enterarme de que los flotantes utilizados en los valores de los contenedores deben compararse utilizando una función de comparación personalizada. Ya he escrito esa función (llamémosla approxEqual(), y suponemos que toma dos flotantes y devuelve True si se juzga que son iguales y False de lo contrario).

Prefiero que los cambios en el código existente se mantengan al mínimo. (Nuevas clases/funciones/etc pueden ser tan complicado como necesario.)

Ejemplo:

if dict1 != dict2: 
    raise DataMismatch 

La condición dict1 != dict2 necesita ser reescrita para que cualquier flotadores utilizados en valores de dict1 y dict2 se comparan utilizando approxEqual función en lugar de __eq__.

El contenido real de los diccionarios proviene de diversas fuentes (análisis de archivos, cálculos, etc.).

Nota: I asked a question earlier acerca de cómo anular el flotador incorporado eq. Esa habría sido una solución fácil, pero me enteré de que Python no permite reemplazar el operador de tipos incorporados __eq__. De ahí esta nueva pregunta.

+0

¿Cuál es tu pregunta? Si ya tienes la función, ¿cuál es el problema? – JoshD

+0

¿Podría proporcionar algún código de ejemplo que espera que funcione de manera diferente? – JoshD

+0

Ya ha respondido: http://stackoverflow.com/questions/3854047/approximate-comparison-in-python – Dingo

Respuesta

9

La única vía para alterar la forma integrada de contenedores comprobar la igualdad es hacerlos contienen como valores, en lugar de los "originales", envuelto valores (envuelto en una clase que anula __eq__ y __ne__). Esto es si necesita modificar la forma en que los contenedores usan la verificación de igualdad, p. para el operador in donde el operando del lado derecho es una lista, así como en el método de los contenedores, como su propio __eq__ (type(x).__eq__(y) es la forma típica en que Python realizará internamente lo que usted codifica como x == y).

Si lo que estás hablando es la realización de sus propios cheques igualdad ( sin alterar las comprobaciones realizadas internamente por los propios contenedores), entonces la única manera de hacerlo es cambiar cada cont1 == cont2 en (por ejemplo) same(cont1, cont2, value_same) donde value_same es una función que acepta dos valores y devuelve True o False como ==. Eso es probablemente demasiado invasivo WRT el criterio que especifique.

Si puede cambiar el contenedor mismos (es decir, el número de lugares donde se crean los objetos contenedores es mucho menor que el número de lugares en los dos contenedores se comprueba la igualdad), a continuación, utilizando una subclase contenedor que anula __eq__ es mejor.

Ej:

class EqMixin(object): 
    def __eq__(self, other): 
    return same(cont1, cont2, value_same) 

(con same siendo como he mencionado en segundo párrafo de la A) y

class EqM_list(EqMixin, list): pass 

(y así sucesivamente para los otros tipos de contenedores que necesita), a continuación, siempre que tengas (p.ej)

x = list(someiter) 

cambio en

x = EqM_list(someiter) 

y estar seguro también de coger otras maneras de crear objetos de la lista, por ejemplo, reemplazar

x = [bah*2 for bah in buh] 

con

x = EqM_list(bah*2 for bah in buh) 

y

x = d.keys() 

con

x = EqM_list(d.iterkeys()) 

y así sucesivamente.

Sí, lo sé, lo que es una molestia - pero es un principio básico (y la práctica ;-) de Python que tipos internos (ya se trate de contenedores, o los tipos de valor, como por ejemplo float) sí mismos no pueden ser cambiados. Esa es una filosofía muy diferente de, por ejemplo, Ruby's y Javascript (y yo personalmente lo prefiero pero veo que a veces puede parecer limitante).

Editar: la petición específica OP parece ser (en términos de esta respuesta) "¿Cómo se implementa same" para los distintos tipos de contenedores, no cómo aplicarlo sin cambiar el == en una llamada de función. Si eso es correcto, entonces (por ejemplo) sin necesidad de utilizar iteradores por simplicidad:

def samelist(a, b, samevalue): 
    if len(a) != len(b): return False 
    return all(samevalue(x, y) for x, y in zip(a, b)) 

def samedict(a, b, samevalue): 
    if set(a) != set(b): return False 
    return all(samevalue(a[x], b[x]) for x in a)) 

Tenga en cuenta que esto se aplica a los valores , conforme a lo solicitado, NO a teclas. "Fuzzying up" la comparación de igualdad de las claves de un dict (o los miembros de un conjunto) es un problema REAL. Mírelo de esta manera: en primer lugar, ¿cómo usted garantía con absoluta certeza que samevalue(a, b) and samevalue(b, c) implica totalmente y asegura samevalue(a, c)? Esta condición de transitividad no se aplica a la mayoría de las "comparaciones difusas" semi-sensibles que he visto, y sin embargo es completamente indispensable para los contenedores basados ​​en tablas hash (como los dicts y sets). Si superas ese obstáculo, entonces surge la pesadilla de hacer que los valores hash de alguna manera "mágicamente" sean consistentes, y ¿qué pasaría si dos claves realmente diferentes en un dict "mapa" de igualdad en este sentido con la misma clave en el otro dict , ¿cuál de los dos valores correspondientes debería usarse entonces ...? Esta locura camino se encuentra, si me preguntas, así que espero que cuando usted dice valores que hace medias, exactamente, valores, y no teclas -)

+0

Sí, quiero decir valores y no claves .. Corrigiendo la pregunta :) – max

+0

@max, oh bien - entonces mi edición debe abordar su q, ¿verdad? –

+0

Sí, no pensé en el problema con las claves que usted mencionó; una vez que lo pensé, me di cuenta de que habría sido un desastre como tú dices.Afortunadamente, nadie usó carrozas como llaves (o en otras palabras, nadie estaba loco), así que todo está bien. – max

Cuestiones relacionadas