pythonsortingpython-2.xtransitivitystrict-weak-ordering

Is there any implementation of Python2 where ordering is transitive?


Is there any existing implementation of Python2 where ordering is transitive? That is, where it's impossible to see this behaviour without creating user-defined types:

>>> x < y < z < x
True

CPython is not transitive because of this counterexample

x = 'b'
y = ()
z = u'ab'

However, this ordering in CPython is documented as only an implementation detail.


Solution

  • Every mainstream Python implementation fails in one way or another except for Skulpt, but it's arguably an incomplete implementation.

    CPython (and variants), PyPy, and Jython:

    >>> 'b' < () < u'ab' < 'b'
    True
    

    IronPython:

    IronPython internally compares the .NET Object.GetHashCode() hashes of unlike objects, so you can break it by abusing the special handling of int and float comparisons and the fact that the internal hash representation of float('+inf') is less than the hash of [] (I'm not sure how stable this is, so it might not work on every installation of IronPython):

    >>> 2**200 < float('+inf') < [] < 2**200
    True
    

    CLPython

    >>> {1: 3} < {1: 4} < {1: 3}
    1
    >>> {1: 3} < {1: 3}
    0
    

    Skulpt

    If you count Skulpt as a complete implementation of Python 2 (it can't compare dictionaries and a few other inconvenient types, and has no unicode type), it actually does work by copying CPython's rules for comparison and conveniently leaving out the unicode type:

    # 1. None is less than anything
    # 2. Sequence types are greater than numeric types
    # 3. [d]ict < [l]ist < [s]tring < [t]uple
    
    >>> None < 0 < {} < [] < '' < ()
    True
    

    For CPython 2, you would actually have [t]uple < [u]nicode, but because unicode and str comparisons are handled as a special case, you lose transitivity. Although it's unlikely that Python 2 will get a patch to fix this "bug", I think you can ensure transitivity by just explicitly changing the order from:

    [d]ict < [l]ist < [s]tring < [t]uple < [u]nicode
    

    To:

    [u]nicode < [d]ict < [l]ist < [s]tring < [t]uple
    

    That way, the special case of str to unicode comparisons doesn't break anything.