I had a list
a = [1, 2, 3]
when I did
a.insert(100, 100)
[1, 2, 3, 100]
as list was originally of siz
Instead of contrasting insert to item assignment, it's more analogous to slice assignment, since both change the size of the list.
>>> a = [1, 2, 3]
>>> a[100:100] = [100]
>>> a
[1, 2, 3, 100]
Slices also don't raise IndexError, and so is consistent with:
a.insert(100, 100)
The documentation says:
L.insert(index, object) # insert object before index
So it seems when you try inserting at index 100 it will really get the existing index on the list before 100.
a.insert(len(a), x)
is supposed to act just like a.append(x)
for connivence. After looking at the source code of the method:
static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
Py_ssize_t i, n = Py_SIZE(self);
...
if (where > n)
where = n;
...
}
You'll see that it handles any int above len(a)
in the same way, by setting any int above n
to n
.
Therefore: any int >= len(a)
will act just like list.append(x)
if passed as the first argument to list.insert(i, x)
.
The official python docs probably only recommends len(a)
as a convenient way to make sure your always inputing a number greater than the length of the list.
From the docs:
list.insert(i, x)
Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x).
So technically when you're doing a.insert(100, 100)
it ensures that 100
will be inserted at a index before 100 which happens to be, in this case, index 3.
Further, we can have a look at the implementation:
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;
}
When you insert a single element into a list, the length of the list will grow by exactly one - not more, not less.
Comments by Guido van Rossum, creator of Python, on the python-dev
mailing list (check the September 2014 archives; in my experience, the exact URLs for specific messages have a way of changing from time to time), in response to OP's crossposting of this question on that list:
On Mon, Sep 15, 2014 at 3:46 PM, Mark Lawrence wrote:
I assume it's based on the concepts of slicing. From the docs "s.insert(i, x) - inserts x into s at the index given by i (same as s[i:i] = [x])".
Ah, right. It matches thigs like s[100:] which is the empty string if s is shorter than 100.
And in another response:
This functionality has existed since the earliest days of Python, and even if we all agreed it was wrong we couldn't change it -- it would just break too much existing code. I can't quite remember why I did it that way but it was definitely a conscious choice; probably some symmetry or edge case. (Note that it works this way at the other end too -- a.insert(-100, x) will insert x at the beginning of a, if a has fewer than 100 elements.)
Ultimately, this kind of thing is a design decision. There are almost always competing concerns, and you can never find something that will be intuitive to everyone. Just look at how many ways different languages handle a concept as fundamental as True and False (in some languages, they are identical to the numbers 1 and 0; in some languages any nonzero is True; in some languages True and False are identical to the characters '1' and '0' (yes, really!); in some languages they are completely incompatible with numbers or any other not-strictly-Boolean type; in some languages empty containers are False, in others they are True; the choices go on and on). Or look at nil/null/None, which also have interesting interactions with Booleans and other calculations. Some languages even have Maybe.
The way Python handles list insertion is handy in some situations, and enough people found it useful that they've written code that makes use of and depends upon insertion behaving this way. Perhaps the documentation could be a little clearer, but it's really not that unclear; and in any case, once you try it, you see what it does, and you write your Python code accordingly.