tags:

views:

32

answers:

1

To implement != and == for my CPython extension type, should I implement tp_compare, tp_richcompare or both?

Under what circumstances is each of them called?

+5  A: 

tp_richcompare is analogous to the rich comparison special methods in the Python language itself. It is chosen in preference to tp_compare when the comparison operators are invoked on the class.

Use tp_richcompare when you want finer control over the comparison logic. For instance, there might be a very cheap way to determine equality, but not precedence. Say memcmp(a, b, sizeof(*a)) == 0 indicates that two very complex data structures are equal, but memcmp(a, b, sizeof(*a)) < 0 doesn't indicate that a is less than b, which instead requires an expensive algorithm to determine. The tp_compare function would require that you always use the expensive algorithm in order to return either a -1, 0 or 1. The tp_richcompare function, OTOH, tells you which comparison is intended, so you can choose either the expensive or the cheap algorithm, depending on the need at hand.

An additional advantage of tp_richcompare is that you can raise exceptions for operations that don't make sense, such as u < v where u and v are points in space.

Marcelo Cantos
+1, well explained!
Alex Martelli
Thanks, that's made it very clear. Am I right in thinking that, if I decide to implement `tp_richcompare`, I should also implement `tp_compare` to support the built-in `cmp` function?
James Hopkin
@James: Good question; I honestly don't know. I will hazard a guess that `cmp` doesn't make use of the rich comparison functions because it would be too awkward, especially since no relationship between the rich comparisons is assumed (`<` need not be the opposite of `>=`).
Marcelo Cantos
@James: No, you don't need `tp_compare` at all if you've got `tp_richcompare`. Witness the many built-in Python types (e.g., `float`) that implement `tp_richcompare` but not `tp_compare`, and yet still work with `cmp`.
Mark Dickinson
And to add to this answer, `tp_compare` is gone in Python 3.x, so `tp_richcompare` is more future-proof, if you care about that.
Mark Dickinson
@James, @Mark: Yes indeed, tp_compare is unnecessary. `cmp` calls `__eq__`, `__lt__` and `__gt__` in turn. Ironically, it returns `1` even if they all return False, which makes me wonder why it bothers to call `__gt__`.
Marcelo Cantos
@Marcelo: I believe that if all of `__lt__`, `__eq__` and `__gt__` return False then the comparison is treated as not implemented, and Python falls back to its default comparison rules involving comparing object id's. So the `__gt__` result isn't ignored, exactly: if it had returned `True` then that would have been used as the basis of the comparison result.
Mark Dickinson
Thanks Mark; I've learned _a few_ new things today!
Marcelo Cantos
Hmm. I just tried `cmp(a, b)` and `cmp(b, a)` for two objects that return False for all their rich comparisons. Both calls returned `1`. Now I'm all confused again!
Marcelo Cantos