Почему диапазон (0) == range (2, 2, 2) True в Python 3?

Почему диапазоны, которые инициализируются разными значениями, сравниваются друг с другом в Python 3?

Когда я выполняю следующие команды в моем интерпретаторе:

>>> r1 = range(0) >>> r2 = range(2, 2, 2) >>> r1 == r2 True 

Результат – True . Почему это так? Почему два разных объекта range с разными значениями параметров считаются равными?

3 Solutions collect form web for “Почему диапазон (0) == range (2, 2, 2) True в Python 3?”

Объекты range являются специальными:

Python будет сравнивать объекты range как последовательности . То, что это по существу означает, заключается в том, что сравнение не оценивает, как они представляют заданную последовательность, а скорее то, что они представляют.

Тот факт, что параметры start , stop и step совершенно разные, не играет здесь никакой разницы, поскольку все они представляют собой пустой список при расширении :

Например, первый объект range :

 list(range(0)) # [] 

и второй объект range :

 list(range(2, 2, 2)) # [] 

Оба представляют собой пустой список, и поскольку два пустых списка сравниваются равными ( True ), то будут отображаться объекты range которые их представляют .

В результате вы можете иметь совершенно разные виды объектов range ; если они представляют одну и ту же последовательность, они будут сравнивать равные:

 range(1, 5, 100) == range(1, 30, 100) 

Оба представляют список с одним элементом [1] поэтому эти два будут сравниваться равными.


Нет, объекты range действительно особенные:

Однако учтите, что, хотя сравнение не оценивает, как они представляют последовательность, результат сравнения может быть достигнут с использованием только значений start , step вместе с len объектов range ; это имеет очень интересные последствия со скоростью сравнений:

 r0 = range(1, 1000000) r1 = range(1, 1000000) l0 = list(r0) l1 = list(r1) 

Диапазоны сравнивают супер быстрый:

 %timeit r0 == r1 The slowest run took 28.82 times longer than the fastest. This could mean that an intermediate result is being cached 10000000 loops, best of 3: 160 ns per loop 

с другой стороны, списки ..

 %timeit l0 == l1 10 loops, best of 3: 27.8 ms per loop 

Да..


Как заметил @SuperBiasedMan , это относится только к объектам диапазона в Python 3. Python 2 range() – это простая функция ol, которая возвращает список, в то время xrange объект 2.x xrange не имеет сравнительных возможностей ( и не только эти .. ), которые имеют объекты range в Python 3.

Посмотрите на ответ @ ajcr для кавычек непосредственно из исходного кода объектов Python 3. В нем задокументировано то, что фактически подразумевает сравнение двух разных диапазонов: простые быстрые операции. Функция range_equals используется в функции range_richcompare для случаев EQ и NE и назначается tp_richcompare для типов PyRange_Type .

Я считаю, что реализация range_equals довольно читабельна (потому что это так просто), чтобы добавить сюда:

 /* r0 and r1 are pointers to rangeobjects */ /* Check if pointers point to same object, example: >>> r1 = r2 = range(0, 10) >>> r1 == r2 obviously returns True. */ if (r0 == r1) return 1; /* Compare the length of the ranges, if they are equal the checks continue. If they are not, False is returned. */ cmp_result = PyObject_RichCompareBool(r0->length, r1->length, Py_EQ); /* Return False or error to the caller >>> range(0, 10) == range(0, 10, 2) fails here */ if (cmp_result != 1) return cmp_result; /* See if the range has a lenght (non-empty). If the length is 0 then due to to previous check, the length of the other range is equal to 0. They are equal. */ cmp_result = PyObject_Not(r0->length); /* Return True or error to the caller. >>> range(0) == range(2, 2, 2) # True (True) gets caught here. Lengths are both zero. */ if (cmp_result != 0) return cmp_result; /* Compare the start values for the ranges, if they don't match then we're not dealing with equal ranges. */ cmp_result = PyObject_RichCompareBool(r0->start, r1->start, Py_EQ); /* Return False or error to the caller. lens are equal, this checks their starting values >>> range(0, 10) == range(10, 20) # False Lengths are equal and non-zero, steps don't match.*/ if (cmp_result != 1) return cmp_result; /* Check if the length is equal to 1. If start is the same and length is 1, they represent the same sequence: >>> range(0, 10, 10) == range(0, 20, 20) # True */ one = PyLong_FromLong(1); if (!one) return -1; cmp_result = PyObject_RichCompareBool(r0->length, one, Py_EQ); Py_DECREF(one); /* Return True or error to the caller. */ if (cmp_result != 0) return cmp_result; /* Finally, just compare their steps */ return PyObject_RichCompareBool(r0->step, r1->step, Py_EQ); 

Я также разбросал некоторые мои собственные комментарии здесь; посмотрите на ответ @ ajcr для эквивалента Python.

Прямая цитата из документов (акцент мой):

Тестирование объектов диапазона для равенства с == и! = Сравнивает их как последовательности. То есть два объекта диапазона считаются равными, если они представляют одну и ту же последовательность значений . (Обратите внимание, что два объекта диапазона, которые сравнивают одинаковые, могут иметь разные атрибуты start, stop и step, например range (0) == range (2, 1, 3) или range (0, 3, 2) == range (0, 4, 2).)

Если вы сравните range s с «тем же» списком, вы получите неравенство, как указано в документах :

Объекты разных типов, кроме разных числовых типов, никогда не сравниваются с равными.

Пример:

 >>> type(range(1)) <class 'range'> >>> type([0]) <class 'list'> >>> [0] == range(1) False >>> [0] == list(range(1)) True 

Обратите внимание, что это явно применимо только к Python 3. В Python 2, где range просто возвращает список, range(1) == [0] оценивается как True .

Чтобы добавить несколько дополнительных подробностей к превосходным ответам на этой странице, два объекта range r0 и r1 сравниваются примерно следующим образом :

 if r0 is r1: # True if r0 and r1 are same object in memory return True if len(r0) != len(r1): # False if different number of elements in sequences return False if not len(r0): # True if r0 has no elements return True if r0.start != r1.start: # False if r0 and r1 have different start values return False if len(r0) == 1: # True if r0 has just one element return True return r0.step == r1.step # if we made it this far, compare step of r0 and r1 

Длина объекта range легко вычисляется с использованием параметров start , stop и step . В случае, когда start == stop , например, Python может сразу знать, что длина равна 0. В нетривиальных случаях Python может просто выполнить простой арифметический расчет, используя значения start , stop и step .

Поэтому в случае range(0) == range(2, 2, 2) , Python делает следующее:

  1. видит, что range(0) и range(2, 2, 2) – разные объекты в памяти.
  2. вычисляет длину обоих объектов; обе длины равны 0 (потому что start == stop в обоих объектах), поэтому необходим другой тест.
  3. видит, что len(range(0)) равно 0. Это означает, что len(range(2, 2, 2)) также равен 0 (предыдущий тест для неравенства не удался), поэтому сравнение должно возвращать True .
  • Почему проверка isinstance (что-то, Mapping) настолько медленная?
  • Как ссылки на переменные разрешаются в Python
  • Как происходит переопределение элементов в кортежах python (a, b) = (b, a)?
  • Как юникод представлен внутри Python?
  • Почему переменная1 + = переменная2 намного быстрее, чем переменная1 = переменная1 + переменная2?
  • Когда del полезно в python?
  • Почему «1000000000000000 в диапазоне (1000000000000001)» так быстро в Python 3?
  • Переменное назначение быстрее, чем один вкладыш
  • Python - лучший язык программирования в мире.