ylab color change python python-3.x range identity python-internals

color - python plotly axis



¿Por qué range(0)== range(2, 2, 2) es verdadero en Python 3? (5)

¿Por qué los rangos que se inicializan con diferentes valores se comparan entre sí en Python 3?

Cuando ejecuto los siguientes comandos en mi intérprete:

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

El resultado es True . ¿Por qué esto es tan? ¿Por qué dos objetos de range diferente con valores de parámetros diferentes se tratan como iguales?


Los objetos de range son especiales:

Python comparará los objetos de range como Sequences . Lo que eso significa esencialmente es que la comparación no evalúa cómo representan una secuencia dada, sino más bien lo que representan.

El hecho de que los parámetros de start , stop y step son completamente diferentes no juega ninguna diferencia aquí porque todos representan una lista vacía cuando se expanden :

Por ejemplo, el primer objeto de range :

list(range(0)) # []

y el segundo objeto de range :

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

Ambos representan una lista vacía y dado que dos listas vacías se comparan igual ( True ) también lo harán los objetos de range que los representan .

Como resultado, puede tener objetos de range aspecto completamente diferente; si representan la misma secuencia, compararán igual:

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

Ambos representan una lista con un solo elemento [1] por lo que estos dos también se compararán igual.

No, los objetos de range son realmente especiales:

Sin embargo, tenga en cuenta que, aunque la comparación no evalúa cómo representan una secuencia, el resultado de la comparación se puede lograr utilizando únicamente los valores de start , step junto con la len de los objetos de range ; Esto tiene implicaciones muy interesantes con la velocidad de las comparaciones:

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

Rangos se compara super rápido:

%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

por otro lado, las listas ...

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

Sí..

Como señaló @SuperBiasedMan , esto solo se aplica a los objetos de rango en Python 3. Python 2 range() es una función simple que devuelve una lista mientras que el objeto xrange 2.x no tiene las capacidades de comparación ( y no solo estas .. ) que tienen los objetos de range en Python 3.

Mire la respuesta de @ ajcr para citas directamente del código fuente en los objetos de range Python 3. Está documentado allí lo que implica la comparación entre dos rangos diferentes: operaciones rápidas simples. La función range_equals se utiliza en la función range_richcompare para casos EQ y NE y se asigna a la ranura PyRange_Type para los tipos PyRange_Type .

Creo que la implementación de range_equals es bastante legible (porque es agradable y simple) para agregar aquí:

/* 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);

También he dispersado algunos de mis propios comentarios aquí; mira la respuesta de @ ajcr para el equivalente de Python.


Cita directa de range (énfasis mío):

La prueba de los objetos de rango para igualdad con == y! = Los compara como secuencias. Es decir, dos objetos de rango se consideran iguales si representan la misma secuencia de valores . (Tenga en cuenta que dos objetos de rango que comparan igual pueden tener diferentes atributos de inicio, parada y paso, por ejemplo rango (0) == rango (2, 1, 3) o rango (0, 3, 2) == rango (0, 4, 2).)

Si compara los range s con la "misma" lista, obtendrá desigualdad, como también se indica en los documentos :

Los objetos de diferentes tipos, excepto diferentes tipos numéricos, nunca se comparan iguales.

Ejemplo:

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

Tenga en cuenta que esto solo se aplica explícitamente a Python 3. En Python 2, donde range solo devuelve una lista, range(1) == [0] evalúa como True .


Para agregar algunos detalles adicionales a las excelentes respuestas en esta página, dos objetos de range r0 y r1 se comparan aproximadamente de la siguiente manera :

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

La longitud de un objeto de range es fácil de calcular utilizando los parámetros de start , stop y step . En el caso donde start == stop , por ejemplo, Python puede saber de inmediato que la longitud es 0. En casos no triviales, Python solo puede hacer un cálculo aritmético simple usando los valores de start , stop y step .

Entonces, en el caso de range(0) == range(2, 2, 2) , Python hace lo siguiente:

  1. ve que range(0) y range(2, 2, 2) son objetos diferentes en la memoria.
  2. calcula la longitud de ambos objetos; ambas longitudes son 0 (porque start == stop en ambos objetos) por lo que se necesita otra prueba.
  3. ve que len(range(0)) es 0. Esto significa que len(range(2, 2, 2)) también es 0 (la prueba anterior de desigualdad falló) y, por lo tanto, la comparación debería devolver True .

range(0) devuelve range(0,0) . Empiezas de 0 a 0 con el paso 1, que no está definido ya que el tercer argumento no puede ser 0 [por defecto]. No puede llegar a 0 con 1. No hay acción de contador en su lugar, por lo tanto, 0.

range(2, 2, 2) devuelve range(2, 2, 2) . Comienzas de 2 a 2 pero con el paso de 2. Que nuevamente, es básicamente 0 ya que no cuentas nada.

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

Cierto y exactamente igual.


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

Dónde:

range(0)

significa el rango de 0 a 0 pasos (aquí el step es igual al valor predeterminado 1 ), lista sin valores.

range(2, 2, 2)

significa el rango de 2 a 2 con paso igual a 2 , lista sin valores.

Entonces, estos rangos son realmente iguales