Remove the @richcmp_method decorator.
I got that now. But at the time I thought __richcmp__ was just a fancy new name for the old __cmp__ in python in order to make it python 3 compatible.
Anyways. It would be nice to say something like that in the documentation. (Maybe I have just missed it and it is there already.)
"
@richcmp_method simply allows to define just
one comparison method __richcmp__ instead of six __eq__, __lt__, ... "
"
and add a reference to some working example somewhere in the sage library.
As many people will come across that file who are not programmers and do not speak cython.
And in future they will also not know about the __cmp__ method of python 2.7
from sage.structure.richcmp import richcmp_by_eq_and_lt,richcmp_method
@richcmp_method
class A(object):
def __init__(self,x):
self._x = x
__richcmp__ = richcmp_by_eq_and_lt("_eq","_lt")
def _eq(self,other):
if type(other)!=type(self):
#other knows how to do the comparison
return other.__eq__(self)
return self._x == other._x
def _lt(self,other):
return self._x<other._x
def __repr__(self):
return "A %r" %self._x
@richcmp_method
class B(A):
#I can do comparison myself
def __init__(self,x,extra):
A.__init__(self,x)
__richcmp__ = richcmp_by_eq_and_lt("_eq","_lt")
def _eq(self,other):
print("hello")
return self._x == other._x
def _lt(self,other):
return self._x<other._x
def __repr__(self):
return "B %r" %self._x
sage: A(1) == B(1,"a")
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-4-f5a54e86b883> in <module>()
----> 1 A(Integer(1)) == B(Integer(1),"a")
/home/simon/sage/src/sage/structure/richcmp.pyx in sage.structure.richcmp.slot_tp_richcompare (build/cythonized/sage/structure/richcmp.c:1459)()
110 Function to put in the ``tp_richcompare`` slot.
111
"""
--> 112 return self.__richcmp__(other, op)
113
114
/home/simon/sage/src/sage/structure/richcmp.pyx in sage.structure.richcmp.richcmp_by_eq_and_lt.richcmp (build/cythonized/sage/structure/richcmp.c:2178)()
317 if equal_types:
318 other_eq = getattr(other, eq_attr)
--> 319 if other_eq(self):
320 return rich_to_bool(op, 0)
321 if op == Py_EQ:
/home/simon/sage/src/sage/modules/bug.py in _eq(self, other)
10 if type(other)!=type(self):
11 #other knows how to do the comparison
---> 12 return other.__eq__(self)
13 return self._x == other._x
14 def _lt(self,other):
... last 3 frames repeated, from the frame below ...
/home/simon/sage/src/sage/structure/richcmp.pyx in sage.structure.richcmp.slot_tp_richcompare (build/cythonized/sage/structure/richcmp.c:1459)()
110 Function to put in the ``tp_richcompare`` slot.
111 """
--> 112 return self.__richcmp__(other, op)
113
114
RuntimeError: maximum recursion depth exceeded while calling a Python object
Enter code here...
from sage.structure.richcmp import richcmp_by_eq_and_lt,richcmp_method
@richcmp_method
class A(object):
def __init__(self,x):
self._x = x
__richcmp__ = richcmp_by_eq_and_lt("_eq","_lt")
def _eq(self,other):
if type(other)!=type(self):
#other knows how to do the comparison
return self.__eq__(other)
return self._x == other._x
def _lt(self,other):
return self._x<other._x
def __repr__(self):
return "A %r" %self._x
@richcmp_method
class B(A):
#I can do comparison myself
def __init__(self,x,extra):
A.__init__(self,x)
__richcmp__ = richcmp_by_eq_and_lt("_eq","_lt")
def _eq(self,other):
print("hello")
return self._x == other._x
def _lt(self,other):
return self._x<other._x
def __repr__(self):
return "B %r" %self._x
### reloading attached file bug.py modified at 12:00:42 ###
sage: A(1) == B(1,"a")
hello
True
sage: V=GF(7)^1
sage: phi=V.hom([V([1])])
sage: phi(V)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-3324c9a5d342> in <module>()
----> 1 phi(V)
/home/simon/sage/src/sage/categories/map.pyx in sage.categories.map.Map.__call__ (build/cythonized/sage/categories/map.c:6738)()
785 return self._call_with_args(x, args, kwds)
786 # Is there coercion?
--> 787 converter = D._internal_coerce_map_from(P)
788 if converter is None:
789 try:
/home/simon/sage/src/sage/structure/parent.pyx in sage.structure.parent.Parent._internal_coerce_map_from (build/cythonized/sage/structure/parent.c:18250)()
2099 return mor
2100
-> 2101 if S == self:
2102 # non-unique parents
2103 if debug.unique_parent_warnings:
/home/simon/sage/src/sage/structure/richcmp.pyx in sage.structure.richcmp.slot_tp_richcompare (build/cythonized/sage/structure/richcmp.c:1459)()
110 Function to put in the ``tp_richcompare`` slot.
111
"""
--> 112 return self.__richcmp__(other, op)
113
114
/home/simon/sage/src/sage/structure/richcmp.pyx in sage.structure.richcmp.richcmp_by_eq_and_lt.richcmp (build/cythonized/sage/structure/richcmp.c:2172)()
317 if equal_types:
318 other_eq = getattr(other, eq_attr)
--> 319 if other_eq(self):
320 return rich_to_bool(op, 0)
321 if op == Py_EQ:
TypeError: _eq_() takes exactly 2 arguments (1 given)
Well richcmp allows you to define just one method. But it does not save you any work at all if the order is partial.
Because then you have to distinguish the cases op = op_LE , op_GE, op_LT ... etc. .... so all that you save is documentation and everything is more obscure.
I am aware of this. It is just unexpected that other.__eq__(self) calls
self._eq(other)
As a user of that rich cmp method I would have expected it to call
other._eq(self).
Raising a not implemented error seems nice. :)
I am aware of this. It is just unexpected that other.__eq__(self) calls
self._eq(other)
As a user of that rich cmp method I would have expected it to call
other._eq(self).
Raising a not implemented error seems nice. :)Note that it should return NotImplemented (a built in constant to be returned by rich comparisons), not raise a NotImplementedError.
> (how often is the first
> thing you do is verify the object has a compatible type?).
Almost never, at least for elements!
For elements (not for parents, though), one is supposed to
implement single underscore arithmetic *and* comparison methods,
which will only be called after coercion took place and so the
two arguments of _richcmp_ are guaranteed to belong to the
same parent.
So, verifying that the types are compatible only applies to
the rare case that elements of a single parent are implemented
by different types, simultaneously. Just never ever override
double underscore __richtcmp__ for elements.