Я работаю над разреженной реализацией списка и недавно реализованным назначением через срез. Это заставило меня обнаружить какое-то поведение в реализации 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.