magic __str__ __getitem__ __eq__ python comparison operator-overloading

__str__ - getattr python



¿Por qué/Cuándo en Python hace `x== y` llamada` y.__ eq__(x) `? (4)

¿Esto no está documentado en la Referencia del lenguaje ? Solo con una mirada rápida, parece que __cmp__ se ignora cuando __eq__ , __lt__ , etc. están definidos. Estoy entendiendo eso para incluir el caso donde __eq__ se define en una clase para padres. str.__eq__ ya está definido así que __cmp__ en sus subclases será ignorado. object.__eq__ etc. no están definidos, por lo que __cmp__ en sus subclases se cumplirán.

En respuesta a la pregunta aclarada:

Sé que __eq__ se llama en preferencia a __cmp__ , pero no estoy claro por qué y.__eq__(x) se llama con preferencia a x.__eq__(y) , cuando este último es lo que el estado de los documentos ocurrirá.

Los documentos dicen que x.__eq__(y) se llamará primero, pero tiene la opción de devolver NotImplemented en cuyo caso se llama y.__eq__(x) . No estoy seguro de por qué confías en que algo diferente está sucediendo aquí.

¿En qué caso estás específicamente desconcertado? Estoy entendiendo que solo estás confundido sobre los casos "b" == tsc y tsc == "b" , ¿correcto? En cualquier caso, str.__eq__(onething, otherthing) se está llamando. Como no anulas el método __eq__ en TestStrCmp, finalmente __eq__ método de la cadena base y te dice que los objetos no son iguales.

Sin conocer los detalles de implementación de str.__eq__ , no sé si ("b").__eq__(tsc) devolverá NotImplemented y le dará a tsc la oportunidad de manejar la prueba de igualdad. Pero incluso si lo hiciera, la forma en que tiene TestStrCmp definido, aún obtendrá un resultado falso.

Entonces no está claro lo que estás viendo aquí que es inesperado.

Quizás lo que está sucediendo es que Python está prefiriendo __eq__ a __cmp__ si está definido en cualquiera de los objetos que se comparan, mientras que usted esperaba que __cmp__ en el objeto __cmp__ a la izquierda tenga prioridad sobre __eq__ en el objeto derecho. ¿Es asi?

Los documentos de Python indican claramente que x==y llama x.__eq__(y) . Sin embargo, parece que bajo muchas circunstancias, lo opuesto es verdad. Dónde está documentado cuándo o por qué sucede esto, y cómo puedo averiguar con certeza si los métodos __cmp__ o __eq__ mi objeto __cmp__ llamados.

Editar: Solo para aclarar, sé que __eq__ se llama en preferencia a __cmp__ , pero no estoy claro por qué y.__eq__(x) se llama con preferencia a x.__eq__(y) , cuando este último es lo que dice el documento pasará.

>>> class TestCmp(object): ... def __cmp__(self, other): ... print "__cmp__ got called" ... return 0 ... >>> class TestEq(object): ... def __eq__(self, other): ... print "__eq__ got called" ... return True ... >>> tc = TestCmp() >>> te = TestEq() >>> >>> 1 == tc __cmp__ got called True >>> tc == 1 __cmp__ got called True >>> >>> 1 == te __eq__ got called True >>> te == 1 __eq__ got called True >>> >>> class TestStrCmp(str): ... def __new__(cls, value): ... return str.__new__(cls, value) ... ... def __cmp__(self, other): ... print "__cmp__ got called" ... return 0 ... >>> class TestStrEq(str): ... def __new__(cls, value): ... return str.__new__(cls, value) ... ... def __eq__(self, other): ... print "__eq__ got called" ... return True ... >>> tsc = TestStrCmp("a") >>> tse = TestStrEq("a") >>> >>> "b" == tsc False >>> tsc == "b" False >>> >>> "b" == tse __eq__ got called True >>> tse == "b" __eq__ got called True

Editar: De la respuesta y el comentario de Mark Dickinson, parece que:

  1. La comparación rica anula __cmp__
  2. __eq__ es su propia __rop__ a su __op__ (y similar para __lt__ , __ge__ , etc.)
  3. Si el objeto izquierdo es una clase incorporada o de estilo nuevo, y la derecha es una subclase de la misma, se intenta __rop__ del objeto __rop__ antes de que __op__ del objeto izquierdo

Esto explica el comportamiento en los ejemplos de TestStrCmp . TestStrCmp es una subclase de str pero no implementa su propio __eq__ por lo que el __eq__ de str tiene prioridad en ambos casos (es decir, tsc == "b" llama b.__eq__(tsc) como __rop__ causa de la regla 1).

En los ejemplos de TestStrEq , se llama tse.__eq__ en ambas instancias porque TestStrEq es una subclase de str y, por lo tanto, se TestStrEq con preferencia.

En los ejemplos de TestEq , TestEq implementa __eq__ y int no, por lo que __eq__ se llama ambas veces (regla 1).

Pero todavía no entiendo el primer ejemplo con TestCmp . tc no es una subclase en int por lo que debe invocarse AFAICT 1.__cmp__(tc) , pero no lo es.


Como sé, __eq__() es un método llamado "comparación enriquecida", y se llama para operadores de comparación con preferencia a __cmp__() continuación. __cmp__() si no se define "comparación rica".

Entonces en A == B:
Si __eq__() se define en A, se llamará
Else __cmp__() se llamará

__eq__() definido en ''str'' por lo que su función __cmp__() no fue llamada.

La misma regla es para __ne__(), __gt__(), __ge__(), __lt__() y __le__() métodos de "comparación rica".


En realidad, en los docs , dice:

[ __cmp__ es c] alled por operaciones de comparación si la comparación rica (ver arriba) no está definida.

__eq__ es un método de comparación rico y, en el caso de TestCmp , no está definido, de ahí la vocación de __cmp__


Falta una excepción clave al comportamiento habitual: cuando el operando de la derecha es una instancia de una subclase de la clase del operando de la izquierda, primero se llama al método especial para el operando de la derecha.

Ver la documentación en:

http://docs.python.org/reference/datamodel.html#coercion-rules

y en particular, los dos párrafos siguientes:

Para los objetos y , primero se intenta x.__op__(y) . Si esto no se implementa o devuelve NotImplemented , se NotImplemented y.__rop__(x) . Si esto tampoco se implementa o devuelve NotImplemented , se NotImplemented una excepción TypeError. Pero mira la siguiente excepción:

Excepción al elemento anterior: si el operando izquierdo es una instancia de un tipo incorporado o una clase de estilo nuevo, y el operando derecho es una instancia de una subclase adecuada de ese tipo o clase y anula el método __rop__() la base , el método __rop__() del operando correcto se prueba antes del método __rop__() del operando izquierdo.