Есть ли реализация Python2, где порядок является транзитивным?
Существует ли какая-либо существующая реализация Python2, где порядок является транзитивным? То есть там, где невозможно увидеть это поведение без создания пользовательских типов:
>>> x < y < z < x
True
CPython не является транзитивным из-за этого контрпример
x = 'b'
y = ()
z = u'ab'
Однако это упорядочение в CPython задокументировано только как деталь реализации.
2 ответа
Каждая основная реализация Python так или иначе дает сбой, за исключением Skulpt, но, возможно, это неполная реализация.
CPython (и варианты), PyPy и Jython:
>>> 'b' < () < u'ab' < 'b'
True
IronPython:
IronPython внутренне сравнивает.NET Object.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
Если вы считаете 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:
Наиболее важные общие правила сравнений приведены в главе 5 " Справочник по языку Python". Выражения / 5.9. Сравнения.
Первые правила хорошо известны для сравнения числовых типов (bool, int, long, float), строк (str, unicode), кортежей и списков. Последние два правила объявляют то, что не указано:
- Сопоставления (словари) сравниваются равными тогда и только тогда, когда их отсортированные списки (ключ, значение) сравниваются равными. [5] Результаты, отличные от равенства, решаются последовательно, но не определяются иначе. [6]
- Большинство других объектов встроенных типов сравниваются неравно, если они не являются тем же объектом; выбор, считать ли один объект меньшим или большим другого, делается произвольно, но последовательно в течение одного выполнения программы.
Многие конкретные правила содержатся в главе " Сравнение" в "Стандартной библиотеке Python" / " Встроенные типы", ссылка на которую приведена выше в вопросе, и в документах о конкретных типах, таких как сложный, десятичный или дробный.
Сравнение порядка не поддерживается для типа complex и должно вызывать TypeError. Десятичный тип сравнивается по значению. Он совместим с числами. Номер начиная с Python 2.7. фракции. Фракция также сравнивается по значению.
Мое размышление: если отношение сравнения может быть произвольным и не воспроизводимым при двух выполнениях программы на одном компьютере, то о транзитивности говорить бесполезно. Все вышеперечисленные случаи, когда порядок явно не указан в Python 2, должны вызывать TypeError в Python 3.
Транзитивность или упорядоченность встроенных типов, как известно, нарушена в Python 2.7 только между типами, в которых реализована эквивалентность значений различных встроенных типов, но они не реализованы эквивалентно другим типам.
Пример: нарушенная транзитивность в IronPython (вдохновлена комментариями Блендера и упрощена):
>>> 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'>