是否有任何Python2的实现,其中排序是可传递的?

时间:2016-10-21 16:16:37

标签: python sorting python-2.x transitivity strict-weak-ordering

Python2的现有实现是否排序为transitive?也就是说,如果不创建用户定义的类型就不可能看到这种行为:

>>> x < y < z < x
True
由于这个反例,CPython不具有传递性

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

但是,CPython中的这种排序仅为documented,只是一个实现细节。

2 个答案:

答案 0 :(得分:3)

除了Skulpt之外,每个主流Python实现都以某种方式失败,但它可以说是一个不完整的实现。

CPython(和变种),PyPy和Jython

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

<强> IronPython的

IronPython在内部比较不同对象的.NET Object.GetHashCode()哈希值,因此您可以通过滥用intfloat比较的特殊处理以及内部哈希表示的事实来打破它float('+inf')的散列值小于[]的散列值(我不确定这是多么稳定,因此它可能不适用于每次安装IronPython):

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

<强> CLPython

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

<强> Skulpt

如果你将Skulpt算作Python 2的完整实现(它无法比较字典和其他一些不方便的类型,并且没有unicode类型),它实际上可以通过复制CPython来实现比较规则,方便地省略unicode类型:

# 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

对于CPython 2,您实际上会[t]uple < [u]nicode,但由于unicodestr比较是作为一种特殊情况处理的,因此会失去传递性。虽然Python 2不太可能获得补丁来修复这个“错误”,但我认为你可以通过明确地改变订单来确保传递性:

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

要:

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

这样,strunicode比较的特殊情况不会破坏任何内容。

答案 1 :(得分:1)

在Python 2.7中,某些比较声明为未指定

比较的最重要的一般规则是 The Python Language Reference第5章表达式/ 5.9 Comparisons

第一个规则是用于比较数字类型(bool,int,long,float),字符串(str,unicode),元组和列表的众所周知的规则。最后两条规则声明了未指定的内容:

  
      
  • 映射(字典)比较相等,当且仅当它们的排序(键,值)列表比较相等时。 [5]除了平等以外的结果一致地得到解决,但没有另外定义。 [6]
  •   
  • 内置类型的大多数其他对象比较不相等,除非它们是同一个对象;选择一个对象是小于还是大于另一个对象的选择是任意,但始终在程序的一次执行中。
  •   

许多具体规则都在 Python标准库 / 内置类型中的比较章节中,在上面的问题中引用,以及有关特定类型的文档,例如复杂十进制 Fraction

complex 类型不支持订单比较,它应该引发TypeError。 十进制类型按值进行比较。它与Python 2.7兼容 numbers.Number fractions.Fraction 也按值进行比较。

我的反思:如果比较关系可以任意并且在同一台计算机上的程序的两次执行中无法再现,则说明传递性是没有用的。所有在Python 2中未明确指定顺序的情况都应该引发Python 3中的TypeError。

内置类型的传递性或排序在Python 2.7中已知,仅在实现不同内置类型的值的等效性但未实现与其他类型等效的类型之间。

示例: IronPython 中的传递性受损(受Blender评论的启发并简化):

>>> assert long(0) < 1.0 < [] < long(0)  # 0 < 1; 'float' < 'list' < 'long'

即使等同于(==),看起来更简单,也 总是可传递的。这打破了运营商的传递性(<=)。请参阅注释中的示例。 (感谢您的解决方案)(等效性不是身份。a is b隐含a == b,但反之亦然。)

示例使用许多简单的用户定义类,名称为单字母大写或小写:

class A(object): pass

class a(object): pass
...

class z(object): pass

观察 - 数字

所有数字类型在CPython和IronPython中都有许多自然等价的值(可能在所有其他文档实现中)

>>>  assert (False == 0 == 0L == 0.0 == 0 + 0j == Decimal('0') == Fraction(0, 1) <
...          True == 1 == 1L == 1.0 == 1 + 0j == Decimal('1') == Fraction(1, 1))

数字类型在CPython中的所有其他类型之前排序:

>>> assert 0 < 10**1000 < float('inf') < A() < Z() < a()

数字类型分散在IronPython

中的其他类型之间
>>> assert D() < decimal.Decimal('0') < E()
>>> assert F() < fractions.Fraction(0, 1) < G()
>>> assert b() < False < c()   # bool
>>> assert c() < 0 + 0j < d()  # complex
>>> assert f() < 0.0 < g()     # float
>>> assert i() < 0 < j()       # int
>>> assert l() < 0L < m()      # long

字符串等

str,bytearray和unicode具有等效值

>>> assert bytearray('ab') == 'ab' == u'ab'

在CPython中使用了对其他类型的订购没有什么特别之处,

>>> assert b() < bytearray('ab') < c()  # bytearray
>>> assert s() < 'ab' < t()             # str
>>> assert u() < u'ab' < v()            # unicode in CPython

在IronPython中:类型unicode的行为类似于str。这并不奇怪,因为字符串在.NET中像unicode一样实现,而且在IronPython中也是如此。

 >>> assert s() < u'ab' < t()           # unicode in Iron Python like str
 >>> unicode
 <type 'str'>