Типы данных
Списки, кортежи, строки, множества и словари — изменяемость, срезы, генераторы коллекций.
34 вопросов
JuniorТеорияОчень частоВ чём разница между list и tuple в Python?
В чём разница между list и tuple в Python?
list изменяемый — можно append, insert или удалять; tuple неизменяемый после создания. Различаются и в CPython: list выделяет память с запасом, поэтому append амортизированно O(1), а tuple фиксированного размера, компактнее и кэшируется через free list.
Типичные ошибки
- ✗Считать
listиtupleодинаковыми под капотом — CPython хранит и оптимизирует их по-разному - ✗Думать, что
tupleполностью неизменяем, даже когда содержит изменяемый элемент вроде списка - ✗Полагать, что кортежи всегда быстрее — выигрыш в памяти и хешируемости, а не в скорости вообще
Уточняющие вопросы
- →Можно ли использовать
tupleкак ключdict? В чём подвох с вложенными изменяемыми? - →Почему
listвыделяет память с запасом, а не растёт на одну ячейку заappend?
JuniorТеорияОчень частоЧто такое отображение (mapping) в Python и какой канонический пример?
Что такое отображение (mapping) в Python и какой канонический пример?
Отображение даёт доступ к значениям по ключам через методы вроде get, keys, values, items. Канонический пример — dict; стандартная библиотека добавляет defaultdict, OrderedDict, Counter. Ключи должны быть хешируемыми, значения — любыми.
Типичные ошибки
- ✗Думать, что значения
dictдолжны быть хешируемыми — только ключи - ✗Считать, что
dictхранит ключи отсортированными; с 3.7 он хранит порядок вставки, а не сортировку - ✗Путать доступ по ключу через хеш с позиционным доступом по индексу
Уточняющие вопросы
- →В чём разница между
d[key]иd.get(key)? - →Что
defaultdictдобавляет к обычномуdict?
JuniorТеорияОчень частоЧто такое set и чем frozenset от него отличается?
Что такое set и чем frozenset от него отличается?
set — неупорядоченная коллекция уникальных хешируемых элементов без индексации и срезов, с быстрой проверкой вхождения и операциями объединения/пересечения/разности. set изменяемый (add, remove); frozenset — неизменяемый хешируемый вариант, годный как ключ dict.
Типичные ошибки
- ✗Ожидать, что множество сохранит порядок вставки или поддержит индексацию — ни того, ни другого
- ✗Пытаться положить
listилиdictв множество — элементы должны быть хешируемыми - ✗Забывать, что только
frozenset(а неset) может быть ключомdictили элементом множества
Уточняющие вопросы
- →Почему элементы множества должны быть хешируемыми?
- →Когда выбрать
frozensetвместоset?
JuniorТеорияОчень частоКак работает срез s[start:stop:step] у последовательности?
Как работает срез s[start:stop:step] у последовательности?
Возвращает новую подпоследовательность от start (включительно) до stop (не включая), беря каждый step-й элемент. Все три необязательны; отрицательные индексы отсчитываются с конца, а s[:] делает поверхностную копию. Выход за границы обрезается, а не падает с ошибкой.
Типичные ошибки
- ✗Ждать, что
stopвключается - ✗Считать, что выход за границы бросает ошибку, а не обрезается
- ✗Думать, что
s[:]— глубокая копия, а не поверхностная
Уточняющие вопросы
- →Что выдаёт отрицательный шаг вроде
s[::-1]и почему? - →Чем присваивание срезу
s[1:3] = [...]отличается от присваивания одному элементу?
JuniorТеорияОчень частоМожно ли изменить отдельный символ в строке Python на месте?
Можно ли изменить отдельный символ в строке Python на месте?
Нет — str неизменяемый. s[0] = 'x' бросает TypeError; любая «правка» вроде replace или + создаёт новый строковый объект и переназначает имя, оставляя исходную строку нетронутой. Именно неизменяемость делает строки хешируемыми и пригодными как ключи dict.
Типичные ошибки
- ✗Пробовать
s[i] = cи ждать, что сработает как у списка - ✗Думать, что
s.replace(...)меняетsна месте, а не возвращает новую строку - ✗Собирать строки через
+=в плотном цикле, не зная, что каждый шаг выделяет новый объект
Уточняющие вопросы
- →Почему многократный
+=для строки в цикле плохо масштабируется и как это исправить? - →Как неизменяемость строк позволяет CPython безопасно интернировать и кэшировать литералы?
MiddleТеорияОчень частоЧто может и не может быть ключом dict и почему?
Что может и не может быть ключом dict и почему?
Ключом может быть любой хешируемый объект: int, str, хешируемый tuple, даже функция или модуль. list, dict, set нельзя — они изменяемы и нехешируемы. Хеш tuple рекурсивен, поэтому tuple с dict внутри тоже нехешируем.
Типичные ошибки
- ✗Думать, что любой
tuple— валидный ключ —tupleсlist/dictвнутри нехешируем - ✗Считать, что любая коллекция запрещена как ключ —
frozensetхешируем и работает - ✗Путать неизменяемость с хешируемостью в требовании к ключу
Уточняющие вопросы
- →Почему
(1, [2])— невалидный ключdict? - →Как добавление классу
__eq__влияет на его пригодность как ключа?
JuniorТеорияЧастоЧто такое последовательность в Python и какие встроенные типы являются последовательностями?
Что такое последовательность в Python и какие встроенные типы являются последовательностями?
Последовательность — упорядоченный итерируемый объект с доступом по целочисленному индексу через __getitem__ и длиной через __len__. Встроенные: list, tuple, range, str, bytes. Поддерживает индексацию, срезы, in, len() и итерацию.
Типичные ошибки
- ✗Путать «последовательность» с «итерируемым» — множества и словари итерируемы, но не последовательности
- ✗Считать, что последовательность обязана быть изменяемой —
tuple,str,rangeнеизменяемы - ✗Забывать, что
strиbytes— последовательности, а не непрозрачные текстовые блоки
Уточняющие вопросы
- →Как работает срез с отрицательным шагом вроде
s[::-1]? - →Что должен принимать
__getitem__, чтобы объект поддерживал срезы?
JuniorКодЧастоЧто выведут эти выражения or / and?
Что выведут эти выражения or / and?
(1) default — or возвращает первый истинный операнд (пустой список ложен). (2) 5 — первое истинное значение. (3) '' — and возвращает первый ложный операнд или последний, если все истинны. or/and возвращают один из операндов, а не bool — основа идиомы x = val or default.
Типичные ошибки
- ✗Считать, что
or/andвсегда возвращают булево - ✗Думать, что они всегда возвращают первый операнд независимо от истинности
- ✗Не знать, что идиома
x = val or defaultопирается на это
Уточняющие вопросы
- →Почему сокращённое вычисление возвращает операнд, а не bool?
- →В чём риск
x = val or default, когдаvalможет быть0или''?
JuniorТеорияЧастоКак работает распаковка a, b, c = seq и звёздочка *rest?
Как работает распаковка a, b, c = seq и звёздочка *rest?
Правая часть итерируется и позиционно привязывается к целям; количество должно совпадать, иначе будет ValueError. Одна звёздная цель вроде a, *rest = seq собирает излишек в list, поэтому может стоять где угодно. Работает с любым итерируемым.
Типичные ошибки
- ✗Забывать, что несовпадение количества бросает
ValueError, а не молча обрезает - ✗Думать, что
*restсобирает вtuple, а не вlist - ✗Считать, что звёздная цель обязана быть последней, а не может стоять где угодно
Уточняющие вопросы
- →Что привяжет
a, *b, c = range(5)к каждому имени? - →Как распаковка заставляет обмен
a, b = b, aработать без временной переменной?
JuniorТеорияЧастоЧто выдают zip, enumerate и range при итерации?
Что выдают zip, enumerate и range при итерации?
range(n) лениво выдаёт целые 0..n-1; enumerate(seq) выдаёт пары (индекс, элемент); zip(a, b) выдаёт кортежи, объединяя элементы, пока не кончится самый короткий вход. Все три — ленивые итераторы в Python 3; оберни в list(), чтобы материализовать. zip(*rows) транспонирует.
Типичные ошибки
- ✗Ждать, что они вернут списки, а не ленивые итераторы в Python 3
- ✗Думать, что
zipдобивает до самого длинного входа, а не останавливается на коротком - ✗Считать, что
enumerateначинает с 1 или чтоrange(n)включаетn
Уточняющие вопросы
- →Как заставить
enumerateначинать счёт с 1? - →Что делает
zip(*matrix)и почему он транспонирует?
MiddleТеорияЧастоЧто возвращают dict.items(), keys() и values() в Python 3?
Что возвращают dict.items(), keys() и values() в Python 3?
Они возвращают динамические объекты-представления, а не списки. Представление отражает изменения dict вживую, поддерживает len(), итерацию и in, а ключи и пары ведут себя как множества. В Python 2 возвращались списки; в Python 3 умолчанием стали представления.
Типичные ошибки
- ✗Думать, что
keys()возвращает список — в Python 3 это живое представление - ✗Править
dictво время итерации его представления — поднимаетRuntimeError - ✗Считать представление одноразовым итератором; оно повторно итерируемо и поддерживает
len()
Уточняющие вопросы
- →Почему итерация представления при изменении
dictможет поднятьRuntimeError? - →В каком смысле представления ключей и пар «как множества»?
MiddleТеорияЧастоЧто делает объект хешируемым в Python?
Что делает объект хешируемым в Python?
Объект хешируемый, если у него есть стабильный __hash__ (не меняется в течение жизни) и __eq__, причём равные объекты обязаны хешироваться одинаково. Неизменяемые встроенные (int, str, хешируемый tuple) подходят; изменяемые list, dict, set — нет.
Типичные ошибки
- ✗Думать, что любой объект с
__eq__хешируем — определение__eq__без__hash__делает его нехешируемым - ✗Считать
tupleвсегда хешируемым — это не так, если он содержит изменяемый элемент - ✗Полагать, что изменяемые объекты можно хешировать, лишь бы их не менять
Уточняющие вопросы
- →Что происходит с хешируемостью, если определить
__eq__, но не__hash__? - →Почему равные объекты обязаны иметь равные хеши?
MiddleКодЧастоЧто выведет эта проверка is против == для целых?
Что выведет эта проверка is против == для целых?
True False, затем True True. CPython кэширует малые целые от −5 до 256, поэтому a и b — один объект (is → True); 257 вне этого диапазона, поэтому c и d — разные объекты (is → False). == сравнивает значение и всегда True. Используйте is лишь для тождества (например, x is None).
Типичные ошибки
- ✗Использовать
isдля сравнения значений целых вместо== - ✗Считать, что равные целые всегда один и тот же объект
- ✗Думать, что
==делегирует кis
Уточняющие вопросы
- →Почему CPython кэширует целые от −5 до 256?
- →Когда
is— правильный выбор вместо==?
MiddleТеорияЧастоПочему поиск в dict/set быстрее, чем в list/tuple?
Почему поиск в dict/set быстрее, чем в list/tuple?
dict и set — хеш-таблицы: поиск в среднем O(1) — Python хеширует ключ и переходит к ячейке. У list и tuple нет индекса по значениям, поэтому x in seq сканирует линейно за O(n). Цена: нужны хешируемые элементы и больше памяти.
Типичные ошибки
- ✗Называть поиск в
dictO(log n) — это в среднем O(1) через хеширование, а не обход дерева - ✗Считать проверку
inдля списка дешёвой — это линейный проход O(n) - ✗Забывать, что хеш-таблицы платят за скорость памятью и хешируемостью
Уточняющие вопросы
- →Какова худшая сложность поиска в
dictи когда она наступает? - →Почему нельзя сделать проверку вхождения за O(1) в
list?
MiddleТеорияЧастоКак Python сравнивает две последовательности операторами < и ==?
Как Python сравнивает две последовательности операторами < и ==?
Поэлементно, слева направо (лексикографически). == истинно, только если длины равны и каждая пара равна. Для < Python сравнивает элементы на первом отличии; если одна сторона кончилась раньше, короткая считается меньшей. Элементы должны быть сравнимы, иначе TypeError.
Типичные ошибки
- ✗Думать, что длинные последовательности автоматически больше
- ✗Считать, что
==сравнивает идентичность, а не значения поэлементно - ✗Ждать, что сравнение элементов разных типов приведёт их, а не бросит
TypeError
Уточняющие вопросы
- →Как разрешается
(1, 2) < (1, 2, 3), когда короткая — префикс длинной? - →Почему сравнение
[1, 'a']с[1, 2]бросает ошибку лишь иногда?
MiddleТеорияЧастоЧем str отличается от bytes, и что делают encode/decode?
Чем str отличается от bytes, и что делают encode/decode?
str — неизменяемая последовательность кодовых точек Unicode; bytes — последовательность сырых октетов 0–255. encode превращает str в bytes через кодек вроде UTF-8; decode делает обратное. Их смешивание или неверный кодек бросает UnicodeError; неявного преобразования в Python 3 нет.
Типичные ошибки
- ✗Путать направление
encode(str→bytes) иdecode(bytes→str) - ✗Ждать неявного преобразования str/bytes, как было в Python 2
- ✗Забывать, что неверный кодек бросает
UnicodeDecodeError, а не молча выдаёт кракозябры
Уточняющие вопросы
- →Почему
'café'.encode('ascii')бросает ошибку и как обработчики вроде'ignore'это меняют? - →Сколько байт занимает не-ASCII символ в UTF-8 против UTF-16?
JuniorКодИногдаЧто выведет print(True + 4) и почему?
Что выведет print(True + 4) и почему?
Выводит 5, 2, 2. В Python bool — подкласс int, где True == 1, а False == 0, поэтому булевы значения участвуют в арифметике напрямую. True + 4 — это 1 + 4, а sum булевых считает количество True. Поэтому суммирование списка условий подсчитывает, сколько из них истинны.
Типичные ошибки
- ✗Думать, что
boolне связан сintи не может участвовать в арифметике - ✗Ожидать, что
True + 4возбудитTypeError - ✗Забывать, что
sumбулевых подсчитывает количествоTrue
Уточняющие вопросы
- →Почему
Trueи1могут схлопнуться в один ключdict? - →Что вернёт
isinstance(True, int)и почему?
JuniorКодИногдаЧто выведут эти цепочечные сравнения?
Что выведут эти цепочечные сравнения?
Все три печатают True. Python связывает сравнения неявным and: (1) это 1 < 2 and 2 < 3; (2) это 3 > 2 and 2 == 2; (3) это (False == False) and (False in [False]) → True and True. Третья удивляет, ведь == и in связываются вместе.
Типичные ошибки
- ✗Читать
a < b < cкак(a < b) < cвместо неявногоand - ✗Не понимать, что
==иinсвязываются вместе в одном выражении - ✗Ожидать, что цепочечное сравнение вернёт операнд, а не bool
Уточняющие вопросы
- →Как Python вычисляет средний операнд цепочки ровно один раз?
- →Почему
False == False in [False]— это не(False == False) in [False]?
JuniorТеорияИногдаЧто такое коллизия хеша?
Что такое коллизия хеша?
Коллизия хеша — это два разных ключа, дающих одно хеш-значение или попадающих в одну ячейку таблицы. dict и set разрешают её пробированием других ячеек, поэтому коллизия стоит скорости поиска, а не корректности — неверное значение не вернётся.
Типичные ошибки
- ✗Думать, что коллизии заставляют
dictвозвращать неверные значения — они лишь добавляют накладные расходы на пробирование - ✗Считать, что встроенные типы никогда не дают коллизий
- ✗Путать равные хеши с равными ключами — равный хеш не значит равный ключ
Уточняющие вопросы
- →Как CPython
dictвыбирает следующую ячейку после коллизии? - →Что происходит с производительностью
dictпо мере роста заполненности?
JuniorКодИногдаПосле b = a на словаре что выведут оба после правок?
После b = a на словаре что выведут оба после правок?
Оба выводят {1: 1, 2: 2, 3: 3}. b = a связывает второе имя с тем же объектом-словарём — не копирует его, поэтому правки через любое имя видны через оба. Для независимого словаря используйте a.copy() или dict(a) (поверхностная копия). a is b здесь True.
Типичные ошибки
- ✗Думать, что
b = aкопирует словарь, а не создаёт псевдоним - ✗Ожидать, что правки через одно имя не затронут другое
- ✗Путать связывание имени с дублированием объекта
Уточняющие вопросы
- →Как сделать
bнезависимой поверхностной копиейa? - →Что вернёт
a is bздесь и что это проверяет?
JuniorКодИногдаЧто даст [1, 2, 3].extend('abc') и почему?
Что даст [1, 2, 3].extend('abc') и почему?
Выводит [1, 2, 3, 'a', 'b', 'c']. extend перебирает свой аргумент и добавляет каждый элемент; строка итерируется по символам, поэтому каждый символ добавляется отдельно. Чтобы добавить всю строку одним элементом, используйте l.append('abc'), дающий [1, 2, 3, 'abc'].
Типичные ошибки
- ✗Ожидать, что
extendдобавит строку одним элементом, какappend - ✗Думать, что
extendотвергает всё, что не является списком - ✗Забывать, что строка итерируется по символам
Уточняющие вопросы
- →Как добавить всю строку одним элементом?
- →Что возбудит
[1, 2].extend(3)и почему?
JuniorКодИногдаПреобразовать список одноэлементных кортежей в список строк
Преобразовать список одноэлементных кортежей в список строк
Берём первый элемент каждого кортежа: [t[0] for t in data]. Одноэлементный кортеж записывается ('x',) с запятой в конце, поэтому индексация [0] достаёт единственное значение. Эквивалентные формы — [v for (v,) in data] (распаковка кортежа в цели цикла) или list(map(lambda t: t[0], data)).
Типичные ошибки
- ✗Забывать, что одноэлементный кортеж требует
[0]для разворачивания - ✗Думать, что
str(t)чисто даёт внутреннее значение - ✗Путать индекс внешнего списка с индексом внутреннего кортежа
Уточняющие вопросы
- →Как развернуть кортежи, которые могут держать больше одного значения?
- →Почему
('x')отличается от('x',)?
JuniorКодИногдаЧто выведет 0.1 + 0.2 == 0.3?
Что выведет 0.1 + 0.2 == 0.3?
False. 0.1 + 0.2 равно 0.30000000000000004, потому что эти значения не представимы точно в двоичной плавающей точке, поэтому сумма отличается от литерала 0.3. Сравнивайте с допуском — math.isclose(0.1 + 0.2, 0.3).
Типичные ошибки
- ✗Считать, что литералы float хранятся точно
- ✗Сравнивать float через
==вместо допуска - ✗Думать, что Python по умолчанию использует десятичную арифметику для
float
Уточняющие вопросы
- →Как
math.iscloseрешает, достаточно ли близки два float? - →Когда стоит взять
decimal.Decimalилиfractions.Fraction?
JuniorКодИногдаКак напечатать литеральные {} внутри f-строки?
Как напечатать литеральные {} внутри f-строки?
Удвойте скобки: print(f'Curly brackets: {{}}') печатает Curly brackets: {}. В f-строке {{ — экранированная литеральная {, а }} — литеральная }; одиночные {...} — поле подстановки, требующее выражения, поэтому пустые {} возбуждают SyntaxError на этапе компиляции.
Типичные ошибки
- ✗Пытаться экранировать скобки обратным слешем вместо удвоения
- ✗Думать, что пустые
{}допустимы и молча пропускаются - ✗Путать экранирование
{{}}в f-строке с особенностямиstr.format
Уточняющие вопросы
- →Как вставить литеральные
{x}рядом с подставляемымx? - →Когда возбуждается
SyntaxErrorдля пустых{}— при компиляции или выполнении?
JuniorКодИногдаЧто выведет отрицательная индексация 'abyz'[-1]?
Что выведет отрицательная индексация 'abyz'[-1]?
Выводит z, затем y. Отрицательный индекс отсчитывается с конца: -1 — последний элемент, -2 — предпоследний. Это эквивалентно var[len(var) - 1]. Работает на любой последовательности — str, list, tuple — а выход за диапазон возбуждает IndexError.
Типичные ошибки
- ✗Думать, что
-1указывает на первый элемент, а не последний - ✗Считать, что отрицательные индексы по умолчанию возбуждают
IndexError - ✗Забывать, что отрицательная индексация работает на любой последовательности, не только строках
Уточняющие вопросы
- →Что вернёт срез
var[-2:]для этой строки? - →Когда отрицательный индекс действительно возбуждает
IndexError?
MiddleКодИногдаЧто выведут += и + для этих связанных списков?
Что выведут += и + для этих связанных списков?
[1, 2, 3, 4], затем [1, 2, 3]. += для списка вызывает __iadd__, изменяя объект на месте, поэтому b (тот же объект) это видит. a = a + [4] строит новый список и переназначает a, оставляя b на исходном — изменение против переназначения.
Типичные ошибки
- ✗Считать, что
+=и+ведут себя одинаково для списков - ✗Думать, что
b = aкопирует список - ✗Не различать изменение на месте и переназначение
Уточняющие вопросы
- →Что возвращает
__iadd__и почему это важно для переназначения? - →Как изменился бы результат, будь
aкортежем, а не списком?
MiddleКодИногдаЧто выведет [[0]*3]*3 после записи?
Что выведет [[0]*3]*3 после записи?
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]. Внешнее * 3 копирует ссылку на один и тот же внутренний список трижды, поэтому изменение одной строки меняет все. Решение — включение, строящее независимые строки: grid = [[0] * 3 for _ in range(3)].
Типичные ошибки
- ✗Считать, что
* 3делает глубокую копию внутреннего списка - ✗Путать умножение списка с трансляцией NumPy
- ✗Не знать решение через включение для независимых строк
Уточняющие вопросы
- →Почему
[[0] * 3 for _ in range(3)]строит независимые строки? - →Применима ли та же ловушка алиасинга к
[0] * 3из неизменяемых int?
MiddleКодИногдаЧто произойдёт при t[0] += [100] на кортеже?
Что произойдёт при t[0] += [100] на кортеже?
(1) проходит → ([1, 99], 2): кортеж неизменяем, но список, на который он ссылается, изменяем. (2) — знаменитая ловушка: += изменяет список И пытается переназначить t[0], поэтому возникает TypeError — но список всё равно изменяется до [1, 99, 100]. Операция выполняется наполовину.
Типичные ошибки
- ✗Думать, что неизменяемость кортежа защищает изменяемые объекты, которые он держит
- ✗Ожидать, что строка (2) оставит список неизменным после ошибки
- ✗Считать, что
TypeErrorоткатывает изменение на месте
Уточняющие вопросы
- →Почему
+=и изменяет список, и пытается присвоить вt[0]? - →Что это говорит о том, действительно ли кортеж «неизменяем»?
MiddleДебаггингРедкоПочему это среднее возвращает неверное значение?
Почему это среднее возвращает неверное значение?
// — это деление с округлением вниз, поэтому average([1, 2]) возвращает 1, а не 1.5. Пустой список также вызывает ZeroDivisionError. Решение: использовать / для истинного деления и защитить пустой случай: return sum(nums) / len(nums) if nums else 0.
Типичные ошибки
- ✗Путать
//(округление вниз) с/(истинное деление) - ✗Думать, что
//округляет к ближайшему, а не к минус бесконечности - ✗Забывать про ZeroDivisionError на пустом списке
Уточняющие вопросы
- →Что делает
//с отрицательными операндами, например-7 // 2? - →Как вычислить целочисленное среднее, всё же округляя корректно?
MiddleДебаггингРедкоПочему этот счётчик слов возбуждает KeyError?
Почему этот счётчик слов возбуждает KeyError?
counts[w] += 1 читает counts[w] до того, как ключ существует → KeyError при первой встрече каждого слова. Решение: counts[w] = counts.get(w, 0) + 1, либо collections.defaultdict(int), либо просто collections.Counter(text.split()).
Типичные ошибки
- ✗Считать, что
dict[k] += 1авто-инициализирует отсутствующий ключ нулём - ✗Винить
splitили возврат, а не чтение отсутствующего ключа - ✗Не использовать
defaultdictилиCounter
Уточняющие вопросы
- →Как
collections.defaultdict(int)меняет поведение при отсутствующем ключе? - →Когда
dict.setdefaultпредпочтительнееgetдля этого паттерна?
MiddleДебаггингРедкоПочему удаление элементов при итерации пропускает их?
Почему удаление элементов при итерации пропускает их?
Удаление элементов при итерации сдвигает индексы под итератором, поэтому он пропускает элементы. Никогда не изменяйте список, по которому идёте. Решение: итерировать копию (for n in nums[:]:) или пересобрать включением: nums = [n for n in nums if n % 2].
Типичные ошибки
- ✗Считать, что удаление оставляет итератор на следующем элементе
- ✗Ожидать RuntimeError на
remove(он про изменение размера, но индексы всё равно сдвигаются) - ✗Изменять список на месте вместо итерации копии или пересборки
Уточняющие вопросы
- →Почему итерация по
nums[:]делает удаление безопасным? - →Как включение вообще избегает изменения списка?
MiddleДебаггингРедкоПочему этот цикл сборки строки медленный на больших данных?
Почему этот цикл сборки строки медленный на больших данных?
Строки неизменяемы, поэтому каждое s += ... строит совершенно новую строку — O(n²) для n слов, медленно на больших данных. Функционально верно, но сложность и есть ошибка. Решение: return ' '.join(words), что даёт O(n) и идиоматично.
Типичные ошибки
- ✗Считать, что
+=на строке изменяет буфер на месте - ✗Винить хвостовой пробел или
stripвместо повторных выделений - ✗Микрооптимизировать конкатенацию вместо использования
join
Уточняющие вопросы
- →Почему неизменяемость строк вынуждает новое выделение на каждое
+=? - →Когда
io.StringIOили список сjoinпредпочтительнее для сборки текста?
SeniorТеорияРедкоКак CPython dict хранит элементы и выполняет ресайз?
Как CPython dict хранит элементы и выполняет ресайз?
CPython dict — разреженная хеш-таблица; ячейка хранит хеш ключа плюс ссылки на ключ и значение. Поиск берёт младшие биты хеша как смещение ячейки и пробирует при коллизии. Около трети ячеек держится пустыми; при заполнении таблица растёт, и записи вставляются заново.
Типичные ошибки
- ✗Думать, что CPython
dictиспользует раздельное связывание — он использует открытую адресацию с пробированием - ✗Считать, что таблица никогда не ресайзится — она растёт, как только заполнится
- ✗Полагать, что слот индексируется полным хешем — сначала только младшими битами
Уточняющие вопросы
- →Зачем держать треть ячеек пустыми, а не заполнять таблицу плотно?
- →Как «компактный dict» в 3.6 изменил раскладку и добавил порядок вставки?
SeniorТеорияРедкоВо что вычисляется {True: 'a', 1: 'b', 1.0: 'c'} и почему?
Во что вычисляется {True: 'a', 1: 'b', 1.0: 'c'} и почему?
Вычисляется в {True: 'c'}. True, 1, 1.0 равны (True == 1 == 1.0) и хешируются одинаково, поэтому это один ключ. Первый вставленный ключ (True) сохраняется, но каждое присваивание перезаписывает значение — итог 'c'.
Типичные ошибки
- ✗Думать, что разные типы означают разные ключи — равенство плюс равный хеш схлопывают их
- ✗Считать, что выживший ключ — последний записанный — это первый вставленный
- ✗Забывать, что значение всё же обновляется при каждом повторном присваивании равному ключу
Уточняющие вопросы
- →Какую идентичность имеет выживший ключ —
Trueили1? - →Как это схлопывание может вызвать реальный баг в таблице поиска?