是否有任何现有的 Python2 实现,其中排序是可传递的?也就是说,如果不创建用户定义的类型就不可能看到这种行为:
>>> x < y < z < x
True
由于这个反例,CPython 不具有传递性
x = 'b'
y = ()
z = u'ab'
但是,CPython 中的这种排序仅记录为实现细节。
除了 Skulpt,每个主流 Python 实现都以某种方式失败,但它可以说是一个不完整的实现。
CPython(和变体)、PyPy 和 Jython:
>>> 'b' < () < u'ab' < 'b'
True
铁蟒:
IronPython 在内部比较不同对象的 .NETObject.GetHashCode()
哈希,因此您可以通过滥用特殊处理int
和float
比较以及内部哈希表示float('+inf')
小于[]
(我不确定这是多么稳定,因此它可能不适用于 IronPython 的每个安装):
>>> 2**200 < float('+inf') < [] < 2**200
True
>>> {1: 3} < {1: 4} < {1: 3}
1
>>> {1: 3} < {1: 3}
0
雕刻
如果您将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
,但是因为unicode
和str
比较是作为一种特殊情况处理的,所以您失去了传递性。尽管 Python 2 不太可能获得修复此“错误”的补丁,但我认为您可以通过显式更改以下顺序来确保传递性:
[d]ict < [l]ist < [s]tring < [t]uple < [u]nicode
至:
[u]nicode < [d]ict < [l]ist < [s]tring < [t]uple
这样,比较的特殊情况str
不会unicode
破坏任何东西。
一些比较在 Python 2.7 中被声明为未指定:
比较最重要的一般规则在 Python 语言参考第 5 章。表达式/5.9比较。
第一个规则是用于比较数字类型(bool、int、long、float)、字符串(str、unicode)、元组和列表的众所周知的规则。最后两条规则声明了未指定的内容:
- 当且仅当它们的排序(键,值)列表比较相等时,映射(字典)比较相等。[5] 平等以外的结果得到一致解决,但没有另外定义。[6]
- 大多数其他内置类型的对象比较不相等,除非它们是同一个对象;一个对象被认为比另一个对象更小还是更大的选择是在程序的一次执行中任意但始终如一地做出的。
许多特定规则在Python Standard Library / Built-in Types的比较章节中,在上面的问题中引用,以及在有关特定类型(如complex、Decimal或Fraction )的文档中。
复杂类型不支持顺序比较,它应该引发 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'>