On __cmp__ specifications and implementation (to mimic it on Jython)
Hi Python developers! First, a quick introduction: My name is Leonardo Soto, I'm a GSoC 2008 student working under PSF and the Jython project, and a Jython commiter since one month ago. As part of what I did on this GSoC, I've improved Jython's __cmp__ a bit. That made me go to look at CPython sources (v2.5.2) to read the implementation of cmp(a,b). Aside from what I've already fixed on Jython, I've found four differences between Jython and CPython cmp(a, b): (1). CPython's cmp() uses a.__cmp__(b) before rich-cmp methods if the types of the two arguments are the same. (2). When using rich-cmp methods, CPython first check if the second type is a subtype of the first, and starts trying with the second method instead of the first. [i.e, the process starts by b.__gt__(a) instead of a.__lt__(b) if type(b) != type(a) and issubclass(b, a)] (3). Similar to above, if the second argument to cmp(a, b) is a old-style instance, and the first isn't, b.__cmp__(a) is used instead of a.__cmp__(b). (4). CPython tries coercion as part of the three way comparison. But doesn't do it exactly as advertised on http://docs.python.org/ref/coercion-rules.html: it does coercion *after* trying a.__cmp__(b), while the docs says *before*. And it only uses the coerced values if both values have the same tp_compare function. I fail to see why, and this is not mentioned on the docs either. [The examples that show this behaviors are at the end of this mail.] Now, my questions: - Are (1), (2) and (3) intentional features? Frankly, Jython being different on (1) and (3) isn't a big deal, but I'd like to match CPython as much as possible (and reasonable). Point (2) is a more interesting, because it seems to follow what http://docs.python.org/ref/coercion-rules.html says for binary ops: "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." But I haven't found any documentation telling that it applies to rich-cmp methods too. Interestingly, it *doesn't* apply to __cmp__ on CPython 2.5.2. BTW, PyPy also fails to follow CPython on points (2) and (3), so it is not just me failing to found the appropriate docs ;-) - What's the idea behind the current implemention of __cmp__ and coercion, as described on (4)? I've to implement this feauture on Jython, but checking that two Java instances have the same __cmp__ method is not that easy as in C. And more important: it could be wrong, depending on what is the idea behind the current CPython implementation. Finally, here are the examples of the behaviors outlined at the start of this mail. (1):
class A(object): ... def __eq__(self, other): return True ... def __cmp__(self, other): return 1 ... cmp(A(), A()) 1
(2):
class A(object): ... def __lt__(self, other): return True ... def __gt__(self, other): return False ... def __eq__(self, other): return False ... class B(A): pass ... cmp(A(), B()) 1
(3):
class A(object): ... def __cmp__(self, other): return 1 ... class B: ... def __cmp__(self, other): return 1 ... cmp(A(), B()) -1
(4):
class A(object): ... def __coerce__(self, other): return 0, 0 ... def __cmp__(self, other): return -1 ... cmp(A(), A()) -1
Regards, -- Leo Soto M. http://blog.leosoto.com
Given that in Python 3.0 __cmp__ is going away, I'm not sure how much
it all matters -- but if you care, as long as it's supported at all,
you might as well strive for the most compatibility with 2.5...
On Tue, Aug 5, 2008 at 1:43 PM, Leo Soto M.
Hi Python developers!
First, a quick introduction: My name is Leonardo Soto, I'm a GSoC 2008 student working under PSF and the Jython project, and a Jython commiter since one month ago.
As part of what I did on this GSoC, I've improved Jython's __cmp__ a bit. That made me go to look at CPython sources (v2.5.2) to read the implementation of cmp(a,b).
Aside from what I've already fixed on Jython, I've found four differences between Jython and CPython cmp(a, b):
(1). CPython's cmp() uses a.__cmp__(b) before rich-cmp methods if the types of the two arguments are the same.
(2). When using rich-cmp methods, CPython first check if the second type is a subtype of the first, and starts trying with the second method instead of the first. [i.e, the process starts by b.__gt__(a) instead of a.__lt__(b) if type(b) != type(a) and issubclass(b, a)]
(3). Similar to above, if the second argument to cmp(a, b) is a old-style instance, and the first isn't, b.__cmp__(a) is used instead of a.__cmp__(b).
(4). CPython tries coercion as part of the three way comparison. But doesn't do it exactly as advertised on http://docs.python.org/ref/coercion-rules.html: it does coercion *after* trying a.__cmp__(b), while the docs says *before*. And it only uses the coerced values if both values have the same tp_compare function. I fail to see why, and this is not mentioned on the docs either.
[The examples that show this behaviors are at the end of this mail.]
Now, my questions:
- Are (1), (2) and (3) intentional features? Frankly, Jython being different on (1) and (3) isn't a big deal, but I'd like to match CPython as much as possible (and reasonable). Point (2) is a more interesting, because it seems to follow what http://docs.python.org/ref/coercion-rules.html says for binary ops:
"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."
But I haven't found any documentation telling that it applies to rich-cmp methods too. Interestingly, it *doesn't* apply to __cmp__ on CPython 2.5.2.
BTW, PyPy also fails to follow CPython on points (2) and (3), so it is not just me failing to found the appropriate docs ;-)
- What's the idea behind the current implemention of __cmp__ and coercion, as described on (4)? I've to implement this feauture on Jython, but checking that two Java instances have the same __cmp__ method is not that easy as in C. And more important: it could be wrong, depending on what is the idea behind the current CPython implementation.
Finally, here are the examples of the behaviors outlined at the start of this mail.
(1):
class A(object): ... def __eq__(self, other): return True ... def __cmp__(self, other): return 1 ... cmp(A(), A()) 1
(2):
class A(object): ... def __lt__(self, other): return True ... def __gt__(self, other): return False ... def __eq__(self, other): return False ... class B(A): pass ... cmp(A(), B()) 1
(3):
class A(object): ... def __cmp__(self, other): return 1 ... class B: ... def __cmp__(self, other): return 1 ... cmp(A(), B()) -1
(4):
class A(object): ... def __coerce__(self, other): return 0, 0 ... def __cmp__(self, other): return -1 ... cmp(A(), A()) -1
Regards, -- Leo Soto M. http://blog.leosoto.com _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
On Tue, Aug 5, 2008 at 4:45 PM, Guido van Rossum
Given that in Python 3.0 __cmp__ is going away, I'm not sure how much it all matters -- but if you care, as long as it's supported at all, you might as well strive for the most compatibility with 2.5...
Sure, it won't be a problem on Jython 3.0. But I'm doing this for the upcoming Jython 2.5, where we still have to live with __cmp__ -- Leo Soto M. http://blog.leosoto.com
On Tue, Aug 5, 2008 at 1:56 PM, Leo Soto M.
On Tue, Aug 5, 2008 at 4:45 PM, Guido van Rossum
wrote: Given that in Python 3.0 __cmp__ is going away, I'm not sure how much it all matters -- but if you care, as long as it's supported at all, you might as well strive for the most compatibility with 2.5...
Sure, it won't be a problem on Jython 3.0. But I'm doing this for the upcoming Jython 2.5, where we still have to live with __cmp__
Which is why I recommend the closest possible compatibility. :-)
-- Leo Soto M. http://blog.leosoto.com
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
On Tue, Aug 5, 2008 at 4:59 PM, Guido van Rossum
On Tue, Aug 5, 2008 at 1:56 PM, Leo Soto M.
wrote: On Tue, Aug 5, 2008 at 4:45 PM, Guido van Rossum
wrote: Given that in Python 3.0 __cmp__ is going away, I'm not sure how much it all matters -- but if you care, as long as it's supported at all, you might as well strive for the most compatibility with 2.5...
Sure, it won't be a problem on Jython 3.0. But I'm doing this for the upcoming Jython 2.5, where we still have to live with __cmp__
Which is why I recommend the closest possible compatibility. :-)
Oh, right :) But that's going to be easier if I understand the "why" and not only the "how". I can live with a "no idea why" answer, though. -- Leo Soto M. http://blog.leosoto.com
On Tue, Aug 5, 2008 at 5:56 PM, Leo Soto M.
On Tue, Aug 5, 2008 at 4:59 PM, Guido van Rossum
wrote: On Tue, Aug 5, 2008 at 1:56 PM, Leo Soto M.
wrote: On Tue, Aug 5, 2008 at 4:45 PM, Guido van Rossum
wrote: Given that in Python 3.0 __cmp__ is going away, I'm not sure how much it all matters -- but if you care, as long as it's supported at all, you might as well strive for the most compatibility with 2.5...
Sure, it won't be a problem on Jython 3.0. But I'm doing this for the upcoming Jython 2.5, where we still have to live with __cmp__
Which is why I recommend the closest possible compatibility. :-)
Oh, right :)
But that's going to be easier if I understand the "why" and not only the "how". I can live with a "no idea why" answer, though.
Close -- I would have to do a lot of thinking, swapping in parts of my memory that have been gone for years. I say, follow CPython blindly and you can't go too wrong: even if the rules are arbitrary, they are what everyone expects. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
On Tue, Aug 5, 2008 at 10:46 PM, Guido van Rossum
On Tue, Aug 5, 2008 at 5:56 PM, Leo Soto M.
wrote: But that's going to be easier if I understand the "why" and not only the "how". I can live with a "no idea why" answer, though.
Close -- I would have to do a lot of thinking, swapping in parts of my memory that have been gone for years. I say, follow CPython blindly and you can't go too wrong: even if the rules are arbitrary, they are what everyone expects.
Fair enough. It is going to be tricky with coercion (AFAIK, there is no easy way to ask two java objects if they share the same method implementation without resorting to reflection), but I'll do my best. Thanks! -- Leo Soto M. http://blog.leosoto.com
participants (2)
-
Guido van Rossum
-
Leo Soto M.