Вставьте список в индекс, который находится далеко за пределами диапазона - ведет себя как добавление
У меня был список
a = [1, 2, 3]
когда я сделал
a.insert(100, 100)
[1, 2, 3, 100]
поскольку список изначально имел размер 4, и я пытался вставить значение в индекс 100, он вел себя как append вместо того, чтобы бросать какие-либо ошибки, поскольку я пытался вставить индекс, который даже не существовал.
Если он не бросает
IndexError: индекс назначения списка вне диапазона
исключение
когда оно выбрасывается, когда
Я пытаюсь сделать
a[100] = 100
Вопрос:
1. Любая идея Почему он был разработан, чтобы молча обращаться с этим, а не информировать пользователя об исключении?
Личное мнение:
Давайте посмотрим, как ведут себя другие языки в такой ситуации:
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]
Как рубины справляются, это довольно ясно и звучит, по крайней мере, для меня.
Java:
В java метод .add(индекс, значение), если применяется с индексом, который выходит за пределы диапазона (например, в arraylist, связанный список)
java.lang.IndexOutOfBoundsException.
Таким образом, я чувствовал, что либо он должен генерировать исключение (как java), либо вставлять нуль в диапазон между ними (поскольку рубин обрабатывает его).
Но бесшумный способ обработки в python просто раскачивается.
ОБНОВЛЕНИЕ (16 сентября 2014 г. IST 8:30):
Как предложил один из респондентов, я разместил этот вопрос в python-dev, и я получил ответ. Это можно увидеть в этом списке рассылки python dev. Если вы обнаружите, что ссылка на нить изменилась, вы можете найти ответ, выполнив google search для названия вопроса, добавленного в начале с помощью python DEV.
Ответы
Ответ 1
Из docs:
list.insert(i, x)
Вставьте элемент в заданную позицию. Первый аргумент - это индекс элемента, перед которым нужно вставить, поэтому a.insert(0, x) вставляет в начале списка и a.insert(len (a), x) эквивалентно a.append(x).
Таким образом, технически, когда вы делаете a.insert(100, 100)
, это гарантирует, что 100
будет вставлен в индекс до 100, который окажется, в данном случае, индексом 3.
Кроме того, мы можем взглянуть на реализацию:
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;
}
Ответ 2
В документации написано:
L.insert(index, object) # insert object before index
Итак, когда вы пытаетесь вставить в индекс 100, он действительно получит существующий индекс в списке до 100.
Ответ 3
Возможно, фактическая реализация прольет некоторый свет.
static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
...
if (where > n)
where = n;
...
}
Итак, это отвечает на вопрос о том, как.
Философски, списки не являются массивами, и есть много манипуляций с списками, которые терпимы к странной индексации. Например, l [1:1000] вернет [2,3]. Все это предназначено для удобства программиста.
Ответ 4
a.insert(len(a), x)
предположительно, чтобы действовать как a.append(x)
для попустительства. Посмотрев исходный код метода:
static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
Py_ssize_t i, n = Py_SIZE(self);
...
if (where > n)
where = n;
...
}
Вы увидите, что он обрабатывает любой int выше len(a)
таким же образом, установив любой int выше n
на n
.
Следовательно: любой int >= len(a)
будет действовать так же, как list.append(x)
, если он передан как первый аргумент list.insert(i, x)
.
Официальные документы python, вероятно, рекомендуют только len(a)
как удобный способ убедиться, что вы всегда вводите число, большее, чем длина списка.
Ответ 5
Когда вы вставляете один элемент в список, длина списка будет возрастать ровно одним - не более, не менее.
Ответ 6
Комментарии Guido van Rossum, создателя Python, в списке рассылки python-dev
(отметьте архив за сентябрь 2014 года, по моему опыту, точные URL-адреса для определенных сообщений могут время от времени меняться), в ответ на перекрестный вызов этого вопроса в этом списке:
В понедельник, 15 сентября, 2014 в 15:46, Марк Лоуренс писал:
Я предполагаю, что это основано на концепциях нарезки. Из документов "s.insert(i, x) - вставляет x в s по индексу, указанному я (так же, как s [i: i] = [x])".
А, правильно. Он соответствует тигам, подобным s [100:], который является пустой строкой, если s менее 100.
И в другом ответе:
Эта функциональность существовала с самых ранних дней Python, и даже если бы мы все согласились, что это было неправильно, мы не могли ее изменить - это просто сломало бы слишком много существующего кода. Я не могу вспомнить, почему я сделал это таким образом, но это был определенно сознательный выбор; вероятно, какой-то симметрии или кромки. (Заметим, что он тоже работает на другом конце - a.insert(-100, x) будет вставлять x в начале a, если a имеет менее 100 элементов.)
В конечном счете, такая вещь - дизайнерское решение. Почти всегда есть конкурирующие проблемы, и вы никогда не сможете найти что-то, что будет интуитивно понятным для всех. Посмотрите, как много разных языков обрабатывают концепцию как фундаментальную, как True и False (на некоторых языках они идентичны числам 1 и 0, а на некоторых языках - ненулевое значение True, а на некоторых языках True и False идентичны символы "1" и "0" (да, действительно!), на некоторых языках они полностью несовместимы с числами или любым другим не-строго-булевым типом, а на некоторых языках пустые контейнеры False, в других - True, выбор продолжается). Или посмотрите на nil/null/None, которые также имеют интересные взаимодействия с булевыми и другими вычислениями. Некоторые языки даже могут быть.
Способ, которым Python обрабатывает вставку списка, удобен в некоторых ситуациях, и достаточно людей считают полезным, что они написали код, который использует и зависит от того, как вставлять себя таким образом. Возможно, документация может быть немного яснее, но это действительно не так уж непонятно; и в любом случае, как только вы попробуете его, вы увидите, что он делает, и вы соответственно напишите свой код Python.
Ответ 7
Вместо того, чтобы контрастировать вставку к назначению элемента, она более аналогична назначению среза, так как оба изменяют размер списка.
>>> a = [1, 2, 3]
>>> a[100:100] = [100]
>>> a
[1, 2, 3, 100]
Срезки также не повышают IndexError, и это согласуется с:
a.insert(100, 100)