Reputation: 10407
Without specifying the equality comparison properties of objects, Python is still doing something when using >
and <
. What is Python actually comparing these objects by if you don't specify __gt__
or __lt__
? I would expect an unsupported operand error here, as you get when trying to add two objects together without defing __add__
.
In [1]: class MyObject(object):
...: pass
...:
In [2]: class YourObject(object):
...: pass
...:
In [3]: me = MyObject()
In [4]: you = YourObject()
In [5]: me > you
Out[5]: False
In [6]: you > me
Out[6]: True
Upvotes: 8
Views: 225
Reputation: 1123970
An arbitrary order is imposed for objects. The ordering is only defined to be stable within a program execution.
This means it's up to the Python implementation to define an ordering when comparing arbitrary objects. CPython uses the memory address if the types are the same (from the C source):
if (v->ob_type == w->ob_type) {
/* When comparing these pointers, they must be cast to
* integer types (i.e. Py_uintptr_t, our spelling of C9X's
* uintptr_t). ANSI specifies that pointer compares other
* than == and != to non-related structures are undefined.
*/
Py_uintptr_t vv = (Py_uintptr_t)v;
Py_uintptr_t ww = (Py_uintptr_t)w;
return (vv < ww) ? -1 : (vv > ww) ? 1 : 0;
}
The same value is the basis for the id()
function, and is also represented in the default repr()
string for custom classes, so it may appear that the repr()
of classes determine ordering. It's only the memory address that does.
For objects that are not the same type, the type name is used instead (with number-like types sorting before others), and if the types differ but their names are the same, the code falls back to the memory address of the type (as opposed to the memory address of the instance when the types are the same).
This implicit ordering has been considered an error in the language, and has been remedied in Python 3:
The ordering comparison operators (
<
,<=
,>=
,>
) raise aTypeError
exception when the operands don’t have a meaningful natural ordering.
This applies to custom classes that do not implement the necessary ordering hooks:
>>> class Foo(): pass
...
>>> Foo() < Foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Foo() < Foo()
Upvotes: 6
Reputation: 17779
EDIT: See the update after reading! The completely correct answer is "undefined, but consistent".
It's testing repr(me)
vs repr(you)
which essentially is doing string comparison. Here is an easy to see example:
class A(object): pass
class B(object): pass
x = A() # <__main__.A object at 0x7f7014e4e2d0>
y = B() # <__main__.B object at 0x7f7014e4e310>
z = A() # <__main__.A object at 0x7f7014e4e390>
x < z < y # True (assuming that the memory addresses are ordered as above)
I agree that this seems a little strange to me. Perhaps there's a good reason for this that I am not aware of?
UPDATE: I am wrong about repr
, though it does look like it uses the base object
repr in test. http://docs.python.org/2/reference/expressions.html#not-in
There is a line there that states "The choice whether one object is considered smaller or larger than another one is made arbitrarily but consistently within one execution of a program." In other words, it might be using repr
but that's not guaranteed. It will, however, be consistently done.
Upvotes: 4
Reputation: 91119
Python uses the object's repr()
if there are no rich comparison operators. (At least, this is valid for Py2; I don't know for Py3).
So if you do
class A(object): pass
class B(object): pass
l = [A() if i % 2 else B() for i in range(1000)]
l.sort()
the objects are sorted by their repr()
, the A()
coming first and then the B()
.
Upvotes: 0