Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom class ordering: no error thrown, what is Python testing for?

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
like image 418
reptilicus Avatar asked Nov 19 '12 15:11

reptilicus


1 Answers

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 a TypeError 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()
like image 57
Martijn Pieters Avatar answered Nov 14 '22 02:11

Martijn Pieters