I just discovered that it's legal in Python to compare arbitrary functions using the operators >
, <
, >=
and <=
. This seems a bit silly; I half expected such a comparison to always be False
(or throw an exception), but the docs say: "Most other objects of built-in types compare unequal unless they are the same object; the choice whether one object is considered smaller or larger than another one is made arbitrarily but consistently within one execution of a program."
So I did a little experimenting, which implied that perhaps the order in which functions are defined is significant here:
>>> def g():
pass
>>> def y():
pass
>>> g > y
False
>>> y > g
True
>>> def r():
pass
>>> g > r
False
>>> r > g
True
>>> y > r
False
>>> r > y
True
>>> def barfoo():
pass
>>> barfoo > r > y > g
True
I tried tracking down the source code (noting here that I'm way out of my depth at this point, having all of two months' experience with C). This answer led me to Python/ceval.c, which seems to handle these comparison operators using PyObject_RichCompare()
(line 4640). I couldn't find a definition for that function, only a PEP, and that's where I'm stuck.
How might I predict the result of these seemingly nonsensical operations? (And as long as we're here... why would I want to?)
Bonus:
>>> unicode > super > object > type > tuple > str > basestring > slice > frozenset > set > xrange > memoryview > long > list > int > staticmethod > classmethod > float > file > reversed > enumerate > dict > property > complex > bytearray > buffer > bool > zip > vars > unichr > sum > sorted > setattr > round > repr > reload > reduce > raw_input > range > pow > ord > open > oct > next > min > max > map > locals > len > iter > issubclass > isinstance > intern > input > id > hex > hash > hasattr > globals > getattr > format > filter > execfile > eval > divmod > dir > delattr > compile > coerce > cmp > chr > callable > bin > apply > any > all > abs > __import__ > help
True
In python2, these type of comparisons is done based on the value of id()
of the object:
In [1]: def g():
...: pass
In [2]: def y():
...: pass
In [3]: g > y
Out[3]: True
In [4]: id(g)
Out[4]: 55898312
In [5]: id(y)
Out[5]: 54420736
The value of id()
normally depends on the memory address of the function object, which might depend on arbitrary things like the previous history of the garbage collector. Probably for this reason, Python's developers removed this functionality, so comparing functions in Python3 now gives an error:
In [3]: g > y
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/home/xxx/<ipython-input-3-9ebc8ff65838> in <module>()
----> 1 g > y
TypeError: unorderable types: function() > function()
In python 3, comparison for equality is still legal, since it does not depend on the arbitrary value of id()
:
In [4]: g == y
Out[4]: False
In [5]: g != y
Out[5]: True
The clue is in:
arbitrarily but consistently
Python does the comparison by id
, so if
str > list
then
id(str) > id(list)
(And I get False
for your bonus!)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With