Explicación detallada del uso de la función de rango de Python|
Los iteradores son (uno de) los 23 patrones de diseño más utilizados. Se pueden encontrar en todas partes en Python. Los usamos con frecuencia, pero no siempre somos conscientes de su existencia. En mi serie sobre iteradores (enlace al final de esta publicación), mencioné al menos 23 formas de generar iteradores. Algunos de estos métodos están diseñados específicamente para generar iteradores, mientras que otros utilizan iteradores "implícitamente" para resolver otros problemas.
Antes de comprender sistemáticamente los iteradores, siempre pensé que el método range() también se usaba para generar iteradores, pero ahora de repente me di cuenta de que genera objetos iterables, ¡no iteradores! (PD: range() en Python2 genera una lista, este artículo está basado en Python3, que genera un objeto iterable)
Por lo tanto, tengo una pregunta: ¿Por qué range() no puede generar un iterador? Mientras buscaba una respuesta, me di cuenta de que tenía algunos malentendidos sobre el tipo de rango. Por lo tanto, en este artículo, obtendremos una comprensión integral del alcance y esperamos explorar y aprender sobre el progreso con usted.
1. ¿Qué es el rango()?
Su sintaxis: rango(inicio, parada [,paso]); inicio se refiere al valor inicial del conteo, el valor predeterminado es 0, parada se refiere al valor final del conteo, pero no incluye la parada; valor; el paso es el paso Largo, el valor predeterminado es 1, no puede ser 0. El método range() genera un rango de números enteros que está cerrado a la izquierda y abierto a la derecha.
Hay varios puntos a tener en cuenta sobre la función range(): (1) Representa un intervalo que está cerrado a la izquierda y abierto a la derecha (2) el parámetro que recibe debe ser un número entero; , que puede ser un número negativo, pero no puede ser un punto flotante y otros tipos; (3) Es un tipo de secuencia inmutable que puede realizar operaciones como juzgar elementos, encontrar elementos, cortar, etc., pero no puede modificar elementos; (4) Es un objeto iterable, pero no un iterador.
2. ¿Por qué range() no puede generar un iterador?
Hay muchos métodos integrados para obtener iteradores, como zip(), enumerate(), map(), filter(), reversed(), etc., pero no existe otro método como range() para obtener solo un objeto Iterable (dígame si hay contraejemplos). Aquí es donde mis conocimientos están fuera de lugar.
Al iterar sobre un bucle for, los iterables y los iteradores funcionan de la misma manera, es decir, se evalúan de forma perezosa, sin diferencia en la complejidad del espacio o del tiempo. Resumo la diferencia entre los dos como "dos cosas diferentes": lo mismo es la iteración diferida, la diferencia es que el objeto iterable no admite el autorecorrido (es decir, el método next ()) y el iterador en sí no admite la fragmentación. (es decir, método getitem ())).
A pesar de estas diferencias, es difícil saber cuál es mejor. Ahora, la sutileza es, ¿por qué diseñar iteradores para los 5 métodos integrados pero un objeto iterable para el método range()? ¿No sería mejor unificarlos?
De hecho, Python ha hecho mucho de esto en aras de la estandarización; por ejemplo, Python2 tenía range() y xrange(), y Python3 eliminó uno de ellos y usó una "alternativa". método. ¿Por qué no ser más formal y dejar que range() genere un iterador?
No he encontrado una explicación oficial para esto, así que lo tomo como una opinión personal.
Los métodos como zip() obtienen parámetros de un determinado objeto iterable. Este es un proceso de reprocesamiento de estos parámetros, por lo que se espera que produzca un determinado resultado de inmediato, por lo que los desarrolladores de Python diseñan el resultado como una iteración. dispositivo. Esto también tiene la ventaja de que el iterador resultante es consumible y no se puede usar incorrectamente cuando cambia el iterable que se pasa como parámetro.
Este no es el caso del método range(). El parámetro recibido por este método no es un objeto iterable. Es en sí mismo un primer proceso, por lo que está diseñado como un objeto iterable y puede usarse. directamente o con Para otros fines de reprocesamiento. Por ejemplo, métodos como zip() son buenos candidatos para utilizar parámetros de tipo rango.
En otras palabras, el método range() es un productor importante y la materia prima que produce tiene muchos usos, por lo que convertirlo en un iterador en las primeras etapas sería un malentendido.
¿Crees que esta explicación es razonable? No dudes en discutir este tema conmigo.
3. ¿Cuál es el tipo de rango?
Esto es "¿Por qué range() no puede generar un iterador? Siguiendo esta línea de pensamiento, miré los objetos de rango que generaba y, una vez que lo hice, me di cuenta de que los objetos de rango no son simples. tampoco.
En primer lugar, ¡es extraño que sea una secuencia inmutable! Nunca me di cuenta de eso, pero nunca pensé en modificar el valor de range()
Sorprendido. , vemos la descripción formal: Hay tres tipos de secuencia básicos: lista, tupla y objeto de rango (Hay tres tipos de secuencia básicos: lista, tupla y objeto de rango)
Esto es algo que no noté , pero resulta que los tipos de rango son en realidad los mismos que las listas y las tuplas. Siempre recordé que las cadenas eran tipos de secuencia inmutables, pero no me di cuenta de que existía un tipo de secuencia inmutable. Cuál es la diferencia entre el rango y otros tipos de secuencias?
Las secuencias ordinarias solo admiten 10 de ellas y no admiten el empalme aditivo ni la repetición multiplicativa.
Entonces la pregunta es: ¿por qué? las cadenas y las tuplas admiten estas dos operaciones, pero las secuencias de rango no. No puedes modificar una secuencia inmutable directamente, pero puedes copiarla en una nueva secuencia, entonces, ¿por qué el objeto de rango ni siquiera admite esta operación? p>
Echemos un vistazo a la documentación oficial:
... Dado que el objeto de rango solo puede representar secuencias estrictas de patrones, mientras que las repeticiones y uniones generalmente violan este patrón estricto... p>
... Dado que los objetos de rango solo pueden representar secuencias que siguen un patrón estricto, las repeticiones y uniones generalmente violan este patrón...
La clave del problema radica en el patrón de. la secuencia de rango.
La clave del problema radica en el patrón de la secuencia de rango. Si lo piensas detenidamente, en realidad es una secuencia aritmética (miau, no me he olvidado de mi escuela secundaria). math...), y concatenar dos secuencias aritméticas o repetir una secuencia aritmética no es una buena idea, razón por la cual el tipo de rango no admite estas dos operaciones, otras modificaciones también destruirán la estructura de la secuencia aritmética, por lo que no lo harán. ser modificado.
4. Resumen
Mirando nuevamente el texto completo, llegué a dos conclusiones frías: el rango es un objeto iterable, no un iterador, el rango es una secuencia isomorfa inmutable;
Si simplemente miras la conclusión, es posible que no te impresione y digas que no es gran cosa. Sin embargo, si pregunto, ¿por qué el rango no es un iterador y por qué el rango es una secuencia inmutable? ¿Puedes dar respuestas a estas dos preguntas para justificar esta idea de diseño? (Nota al margen: he decidido que si alguna vez tengo la oportunidad de entrevistar a alguien, definitivamente haré estas dos preguntas, jaja)
Creo que vale la pena leer este artículo debido a las sutiles e interesantes propiedades de el objeto de rango. Este artículo es parte de una serie sobre iteradores, por lo que no hay muchos conocimientos básicos sobre iteradores, pero hay un tipo especial de iterador que merece su propio artículo: los generadores.