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:
-
ve que
range(0)
yrange(2, 2, 2)
son objetos diferentes en la memoria. -
calcula la longitud de ambos objetos;
ambas longitudes son 0 (porque
start == stop
en ambos objetos) por lo que se necesita otra prueba. -
ve que
len(range(0))
es 0. Esto significa quelen(range(2, 2, 2))
también es 0 (la prueba anterior de desigualdad falló) y, por lo tanto, la comparación debería devolverTrue
.
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