Hello all, I've noticed some strange behaviour of functools.total_ordering decorator, at least it seems strange to me.
Let's consider code snippet below import functools @functools.total_ordering class MyComparableType(object): def __init__(self, value, ref): self.value = value self.ref = ref def __eq__(self, other): # compare two MyComparableType objects or MyComparableType object and some number if type(other) == MyComparableType: return self.value == other.value return self.value == other def __gt__(self, other): # compare two MyComparableType objects or MyComparableType object and some number if type(other) == MyComparableType: return self.value > other.value return self.value > other foo = MyComparableType(10, None) bar = MyComparableType(20, foo) print foo < bar # works ok, True print foo > bar # works ok, False print foo > 5 # works ok, True print foo < 8 # error! we jump into infinite recursion and eventually get stack overflow error It works fine in most cases but crashes when I try to check foo < 8. As I understand the reason why this happens is because of this code in functools.py, total_ordering decorator, which adds missing comparisons definitions ...... '__gt__': [('__lt__', lambda self, other: other > self), ('__ge__', lambda self, other: not other > self), ('__le__', lambda self, other: not self > other)], ....... and python coercion rules say: For objects x and y, first x.__op__(y) is tried. If this is not implemented or returns NotImplemented, y.__rop__(x) is tried. If this is also not implemented or returns NotImplemented, a TypeError exception is raised. But see the following exception: Exception to the previous item: if the left operand is an instance of a built-in type or a new-style class, and the right operand is an instance of a proper subclass of that type or class and overrides the base’s __rop__() method, the right operand’s __rop__() method is tried before the left operand’s __op__() method. So it behaves like it should according to this rule and produces stack overflow because of the infinite recursion calls in the last comparison (foo < 8). There is a way to fix this by replacing missing comparisons definitions in total_ordering to be like this: '__gt__': [('__lt__', lambda self, other: not self > other and not self == other), ('__ge__', lambda self, other: self > other or self == other ), ('__le__', lambda self, other: not self > other)], then script above will work perfectly well in all the cases and there is just one more check added in two of three definitions. -- http://mail.python.org/mailman/listinfo/python-list