Inserte la lista en el índice que está fuera de rango – se comporta como un anexo

Yo tenia una lista

a = [1, 2, 3] 

cuando lo hice

 a.insert(100, 100) [1, 2, 3, 100] 

Como la lista era originalmente de tamaño 4 y estaba tratando de insertar valor en el índice 100, se comportó como un complemento en lugar de lanzar cualquier error mientras intentaba insertar en un índice que ni siquiera existía.

En caso de no tirar

IndexError: índice de asignación de lista fuera de rango

Excepción que arroja cuando bash hacer.

 a[100] = 100 

Pregunta: 1. Cualquier idea ¿Por qué se ha diseñado para manejar esto en silencio en lugar de informar al usuario con una excepción?

Opinión personal :

Veamos cómo se comportan otros idiomas en tal situación:

Ruby:

  > a = [1, 2] > a[100] = 100 > a => [1, 2, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 100] 

La forma en que Ruby maneja esto es bastante clara y suena significativa, al menos para mí.

Java:

En java, el método .add (índice, valor) si se aplica con un índice que está fuera de rango (en arraylist, la lista enlazada por ejemplo) lanzará java.lang.IndexOutOfBoundsException.

Entonces, lo que sentí fue que debería lanzar una excepción (como lo hace Java) o insertar un nulo en el rango intermedio (cuando Ruby lo maneja). Pero la forma silenciosa de manejo en python es simplemente desconcertante.

ACTUALIZACIÓN (16 Sep 2014 IST 8:30 AM):

Como lo sugirió uno de los que respondieron, publiqué esta pregunta en python-dev y obtuve una respuesta. Se puede ver en este hilo de la lista de correo de python dev . Si encuentra que el enlace del hilo ha cambiado, puede encontrar la respuesta haciendo una búsqueda en google para el título de la pregunta que se adjunta al principio con python dev.

De los documentos :

list.insert (i, x)
Insertar un elemento en una posición dada. El primer argumento es el índice del elemento antes del cual se inserta, por lo que a.insert (0, x) se inserta al principio de la lista, y a.insert (len (a), x) es equivalente a a.append ( X).

Así que, técnicamente, cuando estás haciendo a.insert(100, 100) se asegura de que 100 se insertarán en un índice antes de 100, lo que sucede que, en este caso, es el índice 3.

Además, podemos echar un vistazo a la implementación :

 static int ins1(PyListObject *self, Py_ssize_t where, PyObject *v) { Py_ssize_t i, n = Py_SIZE(self); PyObject **items; if (v == NULL) { PyErr_BadInternalCall(); return -1; } if (n == PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "cannot add more objects to list"); return -1; } if (list_resize(self, n+1) == -1) return -1; if (where < 0) { where += n; if (where < 0) where = 0; } if (where > n) // <-- Here the implementation handles indexes > list-len where = n; items = self->ob_item; for (i = n; --i >= where; ) items[i+1] = items[i]; Py_INCREF(v); items[where] = v; return 0; } 

La documentación dice:

 L.insert(index, object) # insert object before index 

Así que parece que cuando intenta insertar en el índice 100 realmente obtendrá el índice existente en la lista antes de 100.

Quizás la implementación real arroje algo de luz.

 static int ins1(PyListObject *self, Py_ssize_t where, PyObject *v) { ... if (where > n) where = n; ... } 

Así que eso responde la pregunta de cómo.

Filosóficamente, las listas no son matrices, y hay muchas manipulaciones de listas que son tolerantes a la indexación extraña. Por ejemplo, l [1: 1000] devolverá [2,3]. Todo esto está pensado como una conveniencia para el progtwigdor.

a.insert(len(a), x) se supone que actúa como a.append(x) por connivencia. Después de mirar el código fuente del método:

 static int ins1(PyListObject *self, Py_ssize_t where, PyObject *v) { Py_ssize_t i, n = Py_SIZE(self); ... if (where > n) where = n; ... } 

Verá que maneja cualquier int por encima de len(a) de la misma manera, estableciendo cualquier int por encima de n en n .

Por lo tanto: cualquier int >= len(a) actuará como list.append(x) si se pasa como el primer argumento para list.insert(i, x) .

La documentación oficial de python probablemente solo recomienda len(a) como una forma conveniente de asegurarse de que siempre ingrese un número mayor que la longitud de la lista.

Comentarios de Guido van Rossum, creador de Python, sobre la lista de correo de python-dev (verifique los archivos de septiembre de 2014 ; en mi experiencia, las URL exactas para mensajes específicos pueden cambiar de vez en cuando), en respuesta al crossposting de OP de esta pregunta en esa lista:

El lunes, 15 de septiembre de 2014 a las 3:46 pm, Mark Lawrence escribió:

Supongo que se basa en los conceptos de rebanar. De los documentos “s.insert (i, x) – inserta x en s en el índice dado por i (igual que s [i: i] = [x])”.

Ah bien. Coincide con thigs como s [100:] que es la cadena vacía si s es más corta que 100.

Y en otra respuesta:

Esta funcionalidad ha existido desde los primeros días de Python, e incluso si todos estuviéramos de acuerdo en que estaba mal, no podríamos cambiarla, simplemente rompería demasiado el código existente. No puedo recordar por qué lo hice así, pero definitivamente fue una elección consciente; Probablemente algún caso de simetría o borde. (Tenga en cuenta que también funciona de esta manera en el otro extremo: a.insert (-100, x) insertará x al comienzo de a, si a tiene menos de 100 elementos).

En última instancia, este tipo de cosas es una decisión de diseño. Casi siempre hay inquietudes que compiten entre sí, y nunca puedes encontrar algo que sea intuitivo para todos. Solo mire de cuántas maneras diferentes idiomas manejan un concepto tan fundamental como Verdadero y Falso (en algunos idiomas, son idénticos a los números 1 y 0; en algunos idiomas cualquier cero es Verdadero; en algunos idiomas, Verdadero y Falso son idénticos a los conceptos los caracteres ‘1’ y ‘0’ (¡sí, en serio!); en algunos idiomas son completamente incompatibles con los números o cualquier otro tipo no estrictamente booleano; en algunos idiomas los contenedores vacíos son falsos, en otros son verdaderos; sigue y sigue). O mira nil / null / None, que también tienen interacciones interesantes con Booleans y otros cálculos. Algunos idiomas incluso tienen Tal vez.

La forma en que Python maneja la inserción de listas es útil en algunas situaciones, y suficientes personas consideraron útil que hayan escrito un código que haga uso y dependa de la inserción que se comporte de esta manera. Quizás la documentación podría ser un poco más clara, pero en realidad no está tan clara; y en cualquier caso, una vez que lo pruebes, verás lo que hace y escribirás tu código de Python en consecuencia.

Cuando inserta un solo elemento en una lista, la longitud de la lista boostá exactamente en uno, ni más, ni menos.

En lugar de contrastar la inserción con la asignación de elementos, es más análogo a la asignación de segmentos, ya que ambos cambian el tamaño de la lista.

 >>> a = [1, 2, 3] >>> a[100:100] = [100] >>> a [1, 2, 3, 100] 

Las rebanadas tampoco generan IndexError, y por lo tanto es consistente con:

 a.insert(100, 100)