R
risboo6909
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.
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.