Я работаю над разреженной реализацией списка и недавно реализованным назначением через срез. Это заставило меня обнаружить какое-то поведение в реализации Python list, которое Я нахожу удивительный.
Учитывая пустой list и назначение через срез:
>>> l = []
>>> l[100:] = ['foo']
Я бы ожидал IndexError от list здесь, потому что способ, которым это реализовано, означает, что элемент не может быть извлечен из указанного индекса::
>>> l[100]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
'foo' не может быть даже извлечен из указанного фрагмента:
>>> l = []
>>> l[100:] = ['foo']
>>> l[100:]
[]
l[100:] = ['foo'] присоединяется к list (т.е. l == ['foo'] после этого назначения) и, похоже, ведет себя таким образом, поскольку исходный BDFL версия. Я не могу найти эту функциональность в любом месте (*), но и CPython и PyPy ведут себя таким образом.
Присвоение по индексу вызывает ошибку:
>>> l[100] = 'bar'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
Итак, почему назначение после конца list через срез не поднимает IndexError (или какая-то другая ошибка, я думаю)?
Чтобы прояснить следующие первые два комментария, этот вопрос относится именно к присваиванию, а не к поиску (cf. Почему индекс подрезки за пределами диапазона работает в Python?).
Вдаваясь в соблазн угадать и присваивать 'foo' до l при индексе 0, когда я явно указал индекс 100, не следует обычным Zen Python.
Рассмотрим случай, когда присвоение происходит далеко от инициализации, а индекс - переменная. Вызывающий абонент больше не может извлекать свои данные из указанного места.
Назначение среза до конца list ведет себя несколько иначе, чем в примере выше:
>>> l = [None, None, None, None]
>>> l[3:] = ['bar']
>>> l[3:]
['bar']
(*) Это поведение определено в Примечание 4 5.6. Типы последовательности в официальной документации (спасибо elethan), но это не объясняет, почему это было бы желательно при назначении.
Примечание.. Я понимаю, как работает поиск, и вы можете видеть, как желательно быть совместимым с этим при назначении, но я искал процитированную причину того, почему приписывание срезу будет вести себя в этом путь. l[100:] возвращает [] сразу после l[100:] = ['foo'], но l[3:] возвращает ['bar'] после l[3:] = ['bar'] поражает, если вы не знаете len(l), особенно если вы следуете за Python EAFP idiom.