Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

Объекты 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 ' функция, которая возвращает список, в то время как объект 2.x xrange не имеет сравнительных возможностей (и не только эти..), что объекты range имеют в Python 3.

Посмотрите @ajcr answer для цитат непосредственно из исходного кода объектов Python 3 range. В нем задокументировано то, что фактически подразумевает сравнение двух разных диапазонов: простые быстрые операции. Функция range_equals используется в range_richcompare function для 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 answer для эквивалента Python.

Ответ 2

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

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

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

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

Пример:

>>> 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.

Ответ 3

Чтобы добавить несколько дополнительных данных к отличным ответам на этой странице, два объекта 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 делает следующее:

  • видит, что range(0) и range(2, 2, 2) - разные объекты в памяти.
  • вычисляет длину обоих объектов; обе длины равны 0 (потому что start == stop в обоих объектах), поэтому требуется еще один тест.
  • видит, что len(range(0)) равно 0. Это означает, что len(range(2, 2, 2)) также равно 0 (предыдущий тест для неравенства не удался), поэтому сравнение должно возвращать True.

Ответ 4

res = range(0) == range(2, 2, 2)

Где:

range(0)

означает диапазон от 0 до 0 - 0 шагов (здесь step равно значению по умолчанию 1), список без значений.

range(2, 2, 2)

означает диапазон от 2 до 2 с шагом равным 2, список без значений.

Итак, эти диапазоны действительно равны

Ответ 5

range(0) возвращает range(0,0). Вы начинаете с 0 до 0 с шага 1, который равен undefined, поскольку третий аргумент не может быть 0 [по умолчанию]. Вы не можете достичь 0 с 1. Нет встречного действия на месте, поэтому 0.

range(2, 2, 2) возвращает range(2, 2, 2). Вы начинаете с 2 до 2, но с шагом 2. Что опять же, в основном 0, так как вы ничего не подсчитываете.

range(0) == range(2,2,2) 

True и точно.